diff --git a/.dockerignore b/.dockerignore
index 7549a71..ab6ea41 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -7,10 +7,15 @@ build/
**/node_modules/
# IDEs and editors
-/.idea
+.idea/
# misc
**/.git*
*.spec
screenshots/
README.md
+
+# runtime data
+data/*
+!data/config.ini
+log/*
diff --git a/Dockerfile b/Dockerfile
index 58889d7..82bc1a2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,20 +14,22 @@ RUN wget https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.xz \
&& ln -s /node-v10.16.0-linux-x64/bin/npm /usr/local/bin/npm
# 后端依赖
-COPY requirements.txt /blivechat/
-RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r /blivechat/requirements.txt
+WORKDIR /blivechat
+COPY requirements.txt ./
+RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# 前端依赖
-WORKDIR /blivechat/frontend
-COPY frontend/package*.json ./
+WORKDIR ./frontend
+COPY frontend/package.json frontend/package-lock.json ./
RUN npm i --registry=https://registry.npm.taobao.org
-# 编译
-COPY . /blivechat
+# 编译前端
+COPY . ../
RUN npm run build
# 运行
-WORKDIR /blivechat
+WORKDIR ..
+VOLUME /blivechat/data /blivechat/log /blivechat/frontend/dist
EXPOSE 12450
ENTRYPOINT ["python3", "main.py"]
CMD ["--host", "0.0.0.0", "--port", "12450"]
diff --git a/README.md b/README.md
index 3d12a0f..7c34d92 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
* 支持自动翻译弹幕、醒目留言到日语
## 使用方法
-### 本地使用
+### 一、本地使用
1. 下载[发布版](https://github.com/xfgryujk/blivechat/releases)(仅提供x64 Windows版)
2. 双击`blivechat.exe`运行服务器,或者用命令行可以指定host和端口号:
```bat
@@ -28,18 +28,25 @@
**注意事项:**
-* 应该先启动blivechat后启动OBS,否则网页会加载失败,这时应该刷新OBS的浏览器源,显示Loaded则加载成功
* 本地使用时不要关闭blivechat.exe那个黑框,否则不能继续获取弹幕
* 样式生成器没有列出所有本地字体,但是可以手动输入本地字体
-### 公共服务器
-请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能出现直播事故
+### 二、公共服务器
+请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能发生直播事故
* [第三方公共服务器](http://chat.bilisc.com/)
* [仅样式生成器](https://style.vtbs.moe/)
-### 源代码版
-1. 编译前端(需要安装Node.js和npm):
+### 三、源代码版(自建服务器或在Windows以外平台)
+0. 由于使用了git子模块,clone时需要加上`--recursive`参数:
+ ```sh
+ git clone --recursive https://github.com/xfgryujk/blivechat.git
+ ```
+ 如果已经clone,拉子模块的方法:
+ ```sh
+ git submodule update --init --recursive
+ ```
+1. 编译前端(需要安装Node.js):
```sh
cd frontend
npm i
@@ -56,8 +63,73 @@
```
3. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略
-### Docker
+### 四、Docker(自建服务器)
1. ```sh
- docker run -d -p 12450:12450 xfgryujk/blivechat:latest
+ docker run --name blivechat -d -p 12450:12450 \
+ --mount source=blc-data,target=/blivechat/data \
+ --mount source=blc-log,target=/blivechat/log \
+ --mount source=blc-frontend,target=/blivechat/frontend/dist \
+ xfgryujk/blivechat:latest
```
2. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略
+
+### nginx配置(可选)
+自建服务器时使用,`sudo vim /etc/nginx/sites-enabled/blivechat.conf`
+
+```conf
+upstream blivechat {
+ keepalive 8;
+ # blivechat地址
+ server 127.0.0.1:12450;
+}
+
+# 强制HTTPS
+server {
+ listen 80;
+ listen [::]:80;
+ server_name YOUR.DOMAIN.NAME;
+
+ return 301 https://$server_name$request_uri;
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ server_name YOUR.DOMAIN.NAME;
+
+ # SSL
+ ssl_certificate /PATH/TO/CERT.crt;
+ ssl_certificate_key /PATH/TO/CERT_KEY.key;
+
+ # 代理header
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header Connection "";
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # 静态文件
+ location / {
+ root /PATH/TO/BLIVECHAT/frontend/dist;
+ # 如果文件不存在,交给前端路由
+ try_files $uri $uri/ /index.html;
+ }
+ # 动态API
+ location /api {
+ proxy_pass http://blivechat;
+ }
+ # websocket
+ location = /api/chat {
+ proxy_pass http://blivechat;
+
+ # 代理websocket必须设置
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+
+ # 由于这个块有proxy_set_header,这些不会自动继承
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+}
+```
diff --git a/api/chat.py b/api/chat.py
index a87ceb7..9ea1ad7 100644
--- a/api/chat.py
+++ b/api/chat.py
@@ -71,7 +71,7 @@ class Room(blivedm.BLiveClient):
def __parse_buy_guard(self, command):
data = command['data']
return self._on_buy_guard(blivedm.GuardBuyMessage(
- data['uid'], data['username'], None, None, None,
+ data['uid'], data['username'], data['guard_level'], None, None,
None, None, data['start_time'], None
))
@@ -149,34 +149,21 @@ class Room(blivedm.BLiveClient):
id_ = uuid.uuid4().hex
# 为了节省带宽用list而不是dict
- self.send_message(Command.ADD_TEXT, [
- # 0: avatarUrl
+ self.send_message(Command.ADD_TEXT, make_text_message(
await models.avatar.get_avatar_url(danmaku.uid),
- # 1: timestamp
int(danmaku.timestamp / 1000),
- # 2: authorName
danmaku.uname,
- # 3: authorType
author_type,
- # 4: content
danmaku.msg,
- # 5: privilegeType
danmaku.privilege_type,
- # 6: isGiftDanmaku
- 1 if danmaku.msg_type else 0,
- # 7: authorLevel
+ danmaku.msg_type,
danmaku.user_level,
- # 8: isNewbie
- 1 if danmaku.urank < 10000 else 0,
- # 9: isMobileVerified
- 1 if danmaku.mobile_verify else 0,
- # 10: medalLevel
+ danmaku.urank < 10000,
+ danmaku.mobile_verify,
0 if danmaku.room_id != self.room_id else danmaku.medal_level,
- # 11: id
id_,
- # 12: translation
translation
- ])
+ ))
if need_translate:
await self._translate_and_response(danmaku.msg, id_)
@@ -206,7 +193,8 @@ class Room(blivedm.BLiveClient):
'id': id_,
'avatarUrl': await models.avatar.get_avatar_url(message.uid),
'timestamp': message.start_time,
- 'authorName': message.username
+ 'authorName': message.username,
+ 'privilegeType': message.guard_level
})
async def _on_super_chat(self, message: blivedm.SuperChatMessage):
@@ -244,8 +232,10 @@ class Room(blivedm.BLiveClient):
})
def _need_translate(self, text):
+ cfg = config.get_config()
return (
- config.get_config().enable_translate
+ cfg.enable_translate
+ and (not cfg.allow_translate_rooms or self.room_id in cfg.allow_translate_rooms)
and self.auto_translate_count > 0
and models.translate.need_translate(text)
)
@@ -266,6 +256,39 @@ class Room(blivedm.BLiveClient):
)
+def make_text_message(avatar_url, timestamp, author_name, author_type, content, privilege_type,
+ is_gift_danmaku, author_level, is_newbie, is_mobile_verified, medal_level,
+ id_, translation):
+ return [
+ # 0: avatarUrl
+ avatar_url,
+ # 1: timestamp
+ timestamp,
+ # 2: authorName
+ author_name,
+ # 3: authorType
+ author_type,
+ # 4: content
+ content,
+ # 5: privilegeType
+ privilege_type,
+ # 6: isGiftDanmaku
+ 1 if is_gift_danmaku else 0,
+ # 7: authorLevel
+ author_level,
+ # 8: isNewbie
+ 1 if is_newbie else 0,
+ # 9: isMobileVerified
+ 1 if is_mobile_verified else 0,
+ # 10: medalLevel
+ medal_level,
+ # 11: id
+ id_,
+ # 12: translation
+ translation
+ ]
+
+
class RoomManager:
def __init__(self):
self._rooms: Dict[int, Room] = {}
@@ -284,8 +307,7 @@ class RoomManager:
if client.auto_translate:
room.auto_translate_count += 1
- if client.application.settings['debug']:
- await client.send_test_message()
+ await client.on_join_room()
def del_client(self, room_id, client: 'ChatHandler'):
room = self._rooms.get(room_id, None)
@@ -390,6 +412,41 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
return True
return super().check_origin(origin)
+ @property
+ def has_joined_room(self):
+ return self.room_id is not None
+
+ def send_message(self, cmd, data):
+ body = json.dumps({'cmd': cmd, 'data': data})
+ try:
+ self.write_message(body)
+ except tornado.websocket.WebSocketClosedError:
+ self.on_close()
+
+ async def on_join_room(self):
+ if self.application.settings['debug']:
+ await self.send_test_message()
+
+ # 不允许自动翻译的提示
+ if self.auto_translate:
+ cfg = config.get_config()
+ if cfg.allow_translate_rooms and self.room_id not in cfg.allow_translate_rooms:
+ self.send_message(Command.ADD_TEXT, make_text_message(
+ models.avatar.DEFAULT_AVATAR_URL,
+ int(time.time()),
+ 'blivechat',
+ 2,
+ 'Translation is not allowed in this room. Please download to use translation',
+ 0,
+ False,
+ 60,
+ False,
+ True,
+ 0,
+ uuid.uuid4().hex,
+ ''
+ ))
+
# 测试用
async def send_test_message(self):
base_data = {
@@ -397,37 +454,25 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
'timestamp': int(time.time()),
'authorName': 'xfgryujk',
}
- text_data = [
- # 0: avatarUrl
+ text_data = make_text_message(
base_data['avatarUrl'],
- # 1: timestamp
base_data['timestamp'],
- # 2: authorName
base_data['authorName'],
- # 3: authorType
0,
- # 4: content
'我能吞下玻璃而不伤身体',
- # 5: privilegeType
0,
- # 6: isGiftDanmaku
- 0,
- # 7: authorLevel
+ False,
20,
- # 8: isNewbie
+ False,
+ True,
0,
- # 9: isMobileVerified
- 1,
- # 10: medalLevel
- 0,
- # 11: id
uuid.uuid4().hex,
- # 12: translation
''
- ]
+ )
member_data = {
**base_data,
- 'id': uuid.uuid4().hex
+ 'id': uuid.uuid4().hex,
+ 'privilegeType': 3
}
gift_data = {
**base_data,
@@ -461,14 +506,3 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
gift_data['totalCoin'] = 1245000
gift_data['giftName'] = '小电视飞船'
self.send_message(Command.ADD_GIFT, gift_data)
-
- @property
- def has_joined_room(self):
- return self.room_id is not None
-
- def send_message(self, cmd, data):
- body = json.dumps({'cmd': cmd, 'data': data})
- try:
- self.write_message(body)
- except tornado.websocket.WebSocketClosedError:
- self.on_close()
diff --git a/api/main.py b/api/main.py
index 1846619..a24ef52 100644
--- a/api/main.py
+++ b/api/main.py
@@ -8,9 +8,15 @@ import update
class MainHandler(tornado.web.StaticFileHandler):
- """为了使用Vue Router的history模式,把所有请求转发到index.html"""
- async def get(self, *args, **kwargs):
- await super().get('index.html', *args, **kwargs)
+ """为了使用Vue Router的history模式,把不存在的文件请求转发到index.html"""
+ async def get(self, path, include_body=True):
+ try:
+ await super().get(path, include_body)
+ except tornado.web.HTTPError as e:
+ if e.status_code != 404:
+ raise
+ # 不存在的文件请求转发到index.html,交给前端路由
+ await super().get('index.html', include_body)
# noinspection PyAbstractClass
@@ -20,6 +26,7 @@ class ServerInfoHandler(api.base.ApiHandler):
self.write({
'version': update.VERSION,
'config': {
- 'enableTranslate': cfg.enable_translate
+ 'enableTranslate': cfg.enable_translate,
+ 'loaderUrl': cfg.loader_url
}
})
diff --git a/blivedm b/blivedm
index d173228..4c64c1b 160000
--- a/blivedm
+++ b/blivedm
@@ -1 +1 @@
-Subproject commit d173228c5f83c2f5f94551259e0e6c01e929d92c
+Subproject commit 4c64c1bd1e9fe634894d7b781eab1fef0e753907
diff --git a/config.py b/config.py
index 47f348f..ec3dc92 100644
--- a/config.py
+++ b/config.py
@@ -13,14 +13,20 @@ _config: Optional['AppConfig'] = None
def init():
- reload()
+ if reload():
+ return
+ logger.warning('Using default config')
+ global _config
+ _config = AppConfig()
def reload():
config = AppConfig()
- if config.load(CONFIG_PATH):
- global _config
- _config = config
+ if not config.load(CONFIG_PATH):
+ return False
+ global _config
+ _config = config
+ return True
def get_config():
@@ -31,14 +37,29 @@ class AppConfig:
def __init__(self):
self.database_url = 'sqlite:///data/database.db'
self.enable_translate = True
+ self.allow_translate_rooms = {}
+ self.tornado_xheaders = False
+ self.loader_url = ''
def load(self, path):
- config = configparser.ConfigParser()
- config.read(path)
try:
+ config = configparser.ConfigParser()
+ config.read(path)
+
app_section = config['app']
self.database_url = app_section['database_url']
self.enable_translate = app_section.getboolean('enable_translate')
+
+ allow_translate_rooms = app_section['allow_translate_rooms']
+ if allow_translate_rooms == '':
+ self.allow_translate_rooms = {}
+ else:
+ allow_translate_rooms = allow_translate_rooms.split(',')
+ self.allow_translate_rooms = set(map(lambda id_: int(id_.strip()), allow_translate_rooms))
+
+ self.tornado_xheaders = app_section.getboolean('tornado_xheaders')
+ self.loader_url = app_section['loader_url']
+
except (KeyError, ValueError):
logger.exception('Failed to load config:')
return False
diff --git a/data/config.ini b/data/config.ini
index 8e6edd6..b0df8ce 100644
--- a/data/config.ini
+++ b/data/config.ini
@@ -1,11 +1,25 @@
[app]
# See https://docs.sqlalchemy.org/en/13/core/engines.html#database-urls
database_url = sqlite:///data/database.db
+
# Enable auto translate to Japanese
enable_translate = true
+# Comma separated room IDs in which translation are not allowed. If empty, all are allowed
+# Example: allow_translate_rooms = 4895312,22347054,21693691
+allow_translate_rooms =
+
+# Set to true if you are using a reverse proxy server such as nginx
+tornado_xheaders = false
+
+# Use a loader so that you can run OBS before blivechat. If empty, no loader is used
+loader_url = https://xfgryujk.sinacloud.net/blivechat/loader.html
+
# DON'T modify this section
[DEFAULT]
database_url = sqlite:///data/database.db
enable_translate = true
+allow_translate_rooms =
+tornado_xheaders = false
+loader_url =
diff --git a/frontend/src/api/config.js b/frontend/src/api/config.js
index 96c1c7e..a4a44a2 100644
--- a/frontend/src/api/config.js
+++ b/frontend/src/api/config.js
@@ -5,7 +5,7 @@ export const DEFAULT_CONFIG = {
showDanmaku: true,
showGift: true,
showGiftName: false,
- mergeSimilarDanmaku: true,
+ mergeSimilarDanmaku: false,
mergeGift: true,
maxNumber: 60,
diff --git a/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue b/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue
deleted file mode 100644
index ebd7cc5..0000000
--- a/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-
-
-
-
-
-
-
{{authorName}}
-
{{title}}
-
{{content}}
-
-
{{timeText}}
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/components/ChatRenderer/MembershipItem.vue b/frontend/src/components/ChatRenderer/MembershipItem.vue
new file mode 100644
index 0000000..3bd5525
--- /dev/null
+++ b/frontend/src/components/ChatRenderer/MembershipItem.vue
@@ -0,0 +1,421 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/ChatRenderer/PaidMessage.vue b/frontend/src/components/ChatRenderer/PaidMessage.vue
index 5c87d2e..4809d7d 100644
--- a/frontend/src/components/ChatRenderer/PaidMessage.vue
+++ b/frontend/src/components/ChatRenderer/PaidMessage.vue
@@ -56,7 +56,7 @@ export default {
return 'CN¥' + utils.formatCurrency(this.price)
},
timeText() {
- return utils.getTimeTextMinSec(this.time)
+ return utils.getTimeTextHourMin(this.time)
}
}
}
diff --git a/frontend/src/components/ChatRenderer/TextMessage.vue b/frontend/src/components/ChatRenderer/TextMessage.vue
index c3c3a10..6d0ebd7 100644
--- a/frontend/src/components/ChatRenderer/TextMessage.vue
+++ b/frontend/src/components/ChatRenderer/TextMessage.vue
@@ -54,7 +54,7 @@ export default {
},
computed: {
timeText() {
- return utils.getTimeTextMinSec(this.time)
+ return utils.getTimeTextHourMin(this.time)
},
authorTypeText() {
return constants.AUTHOR_TYPE_TO_TEXT[this.authorType]
diff --git a/frontend/src/components/ChatRenderer/Ticker.vue b/frontend/src/components/ChatRenderer/Ticker.vue
index 28abd49..0f8e459 100644
--- a/frontend/src/components/ChatRenderer/Ticker.vue
+++ b/frontend/src/components/ChatRenderer/Ticker.vue
@@ -26,11 +26,11 @@
-
+ :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName" :privilegeType="pinnedMessage.privilegeType"
+ :title="pinnedMessage.title" :time="pinnedMessage.time"
+ >
-
+ :avatarUrl="message.avatarUrl" :authorName="message.authorName" :privilegeType="message.privilegeType"
+ :title="message.title" :time="message.time"
+ >
1000) {
- sleepTime = 1000
- } else if (sleepTime < 80) {
- sleepTime = 80
+ if (sleepTime > MAX_SLEEP_TIME) {
+ sleepTime = MAX_SLEEP_TIME
+ } else if (sleepTime < MIN_SLEEP_TIME) {
+ sleepTime = MIN_SLEEP_TIME
}
} else {
// 按最快速度发
- sleepTime = 80
+ sleepTime = MIN_SLEEP_TIME
}
this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages, sleepTime)
},
@@ -628,6 +638,288 @@ html:not(.style-scope) {
--yt-pdg-paid-stickers-author-name-font-size: 13px;
--yt-pdg-paid-stickers-margin-left: 38px;
}
+
+html:not(.style-scope) {
+ --layout_-_display: flex;
+ ;
+
+ --layout-inline_-_display: inline-flex;
+ ;
+
+ --layout-horizontal_-_display: var(--layout_-_display);
+ --layout-horizontal_-_-ms-flex-direction: row;
+ --layout-horizontal_-_-webkit-flex-direction: row;
+ --layout-horizontal_-_flex-direction: row;
+ ;
+
+ --layout-horizontal-reverse_-_display: var(--layout_-_display);
+ --layout-horizontal-reverse_-_-ms-flex-direction: row-reverse;
+ --layout-horizontal-reverse_-_-webkit-flex-direction: row-reverse;
+ --layout-horizontal-reverse_-_flex-direction: row-reverse;
+ ;
+
+ --layout-vertical_-_display: var(--layout_-_display);
+ --layout-vertical_-_-ms-flex-direction: column;
+ --layout-vertical_-_-webkit-flex-direction: column;
+ --layout-vertical_-_flex-direction: column;
+ ;
+
+ --layout-vertical-reverse_-_display: var(--layout_-_display);
+ --layout-vertical-reverse_-_-ms-flex-direction: column-reverse;
+ --layout-vertical-reverse_-_-webkit-flex-direction: column-reverse;
+ --layout-vertical-reverse_-_flex-direction: column-reverse;
+ ;
+
+ --layout-wrap_-_-ms-flex-wrap: wrap;
+ --layout-wrap_-_-webkit-flex-wrap: wrap;
+ --layout-wrap_-_flex-wrap: wrap;
+ ;
+
+ --layout-wrap-reverse_-_-ms-flex-wrap: wrap-reverse;
+ --layout-wrap-reverse_-_-webkit-flex-wrap: wrap-reverse;
+ --layout-wrap-reverse_-_flex-wrap: wrap-reverse;
+ ;
+
+ --layout-flex-auto_-_-ms-flex: 1 1 auto;
+ --layout-flex-auto_-_-webkit-flex: 1 1 auto;
+ --layout-flex-auto_-_flex: 1 1 auto;
+ ;
+
+ --layout-flex-none_-_-ms-flex: none;
+ --layout-flex-none_-_-webkit-flex: none;
+ --layout-flex-none_-_flex: none;
+ ;
+
+ --layout-flex_-_-ms-flex: 1 1 0.000000001px;
+ --layout-flex_-_-webkit-flex: 1;
+ --layout-flex_-_flex: 1;
+ --layout-flex_-_-webkit-flex-basis: 0.000000001px;
+ --layout-flex_-_flex-basis: 0.000000001px;
+ ;
+
+ --layout-flex-2_-_-ms-flex: 2;
+ --layout-flex-2_-_-webkit-flex: 2;
+ --layout-flex-2_-_flex: 2;
+ ;
+
+ --layout-flex-3_-_-ms-flex: 3;
+ --layout-flex-3_-_-webkit-flex: 3;
+ --layout-flex-3_-_flex: 3;
+ ;
+
+ --layout-flex-4_-_-ms-flex: 4;
+ --layout-flex-4_-_-webkit-flex: 4;
+ --layout-flex-4_-_flex: 4;
+ ;
+
+ --layout-flex-5_-_-ms-flex: 5;
+ --layout-flex-5_-_-webkit-flex: 5;
+ --layout-flex-5_-_flex: 5;
+ ;
+
+ --layout-flex-6_-_-ms-flex: 6;
+ --layout-flex-6_-_-webkit-flex: 6;
+ --layout-flex-6_-_flex: 6;
+ ;
+
+ --layout-flex-7_-_-ms-flex: 7;
+ --layout-flex-7_-_-webkit-flex: 7;
+ --layout-flex-7_-_flex: 7;
+ ;
+
+ --layout-flex-8_-_-ms-flex: 8;
+ --layout-flex-8_-_-webkit-flex: 8;
+ --layout-flex-8_-_flex: 8;
+ ;
+
+ --layout-flex-9_-_-ms-flex: 9;
+ --layout-flex-9_-_-webkit-flex: 9;
+ --layout-flex-9_-_flex: 9;
+ ;
+
+ --layout-flex-10_-_-ms-flex: 10;
+ --layout-flex-10_-_-webkit-flex: 10;
+ --layout-flex-10_-_flex: 10;
+ ;
+
+ --layout-flex-11_-_-ms-flex: 11;
+ --layout-flex-11_-_-webkit-flex: 11;
+ --layout-flex-11_-_flex: 11;
+ ;
+
+ --layout-flex-12_-_-ms-flex: 12;
+ --layout-flex-12_-_-webkit-flex: 12;
+ --layout-flex-12_-_flex: 12;
+ ;
+
+
+
+ --layout-start_-_-ms-flex-align: start;
+ --layout-start_-_-webkit-align-items: flex-start;
+ --layout-start_-_align-items: flex-start;
+ ;
+
+ --layout-center_-_-ms-flex-align: center;
+ --layout-center_-_-webkit-align-items: center;
+ --layout-center_-_align-items: center;
+ ;
+
+ --layout-end_-_-ms-flex-align: end;
+ --layout-end_-_-webkit-align-items: flex-end;
+ --layout-end_-_align-items: flex-end;
+ ;
+
+ --layout-baseline_-_-ms-flex-align: baseline;
+ --layout-baseline_-_-webkit-align-items: baseline;
+ --layout-baseline_-_align-items: baseline;
+ ;
+
+
+
+ --layout-start-justified_-_-ms-flex-pack: start;
+ --layout-start-justified_-_-webkit-justify-content: flex-start;
+ --layout-start-justified_-_justify-content: flex-start;
+ ;
+
+ --layout-center-justified_-_-ms-flex-pack: center;
+ --layout-center-justified_-_-webkit-justify-content: center;
+ --layout-center-justified_-_justify-content: center;
+ ;
+
+ --layout-end-justified_-_-ms-flex-pack: end;
+ --layout-end-justified_-_-webkit-justify-content: flex-end;
+ --layout-end-justified_-_justify-content: flex-end;
+ ;
+
+ --layout-around-justified_-_-ms-flex-pack: distribute;
+ --layout-around-justified_-_-webkit-justify-content: space-around;
+ --layout-around-justified_-_justify-content: space-around;
+ ;
+
+ --layout-justified_-_-ms-flex-pack: justify;
+ --layout-justified_-_-webkit-justify-content: space-between;
+ --layout-justified_-_justify-content: space-between;
+ ;
+
+ --layout-center-center_-_-ms-flex-align: var(--layout-center_-_-ms-flex-align);
+ --layout-center-center_-_-webkit-align-items: var(--layout-center_-_-webkit-align-items);
+ --layout-center-center_-_align-items: var(--layout-center_-_align-items);
+ --layout-center-center_-_-ms-flex-pack: var(--layout-center-justified_-_-ms-flex-pack);
+ --layout-center-center_-_-webkit-justify-content: var(--layout-center-justified_-_-webkit-justify-content);
+ --layout-center-center_-_justify-content: var(--layout-center-justified_-_justify-content);
+ ;
+
+
+
+ --layout-self-start_-_-ms-align-self: flex-start;
+ --layout-self-start_-_-webkit-align-self: flex-start;
+ --layout-self-start_-_align-self: flex-start;
+ ;
+
+ --layout-self-center_-_-ms-align-self: center;
+ --layout-self-center_-_-webkit-align-self: center;
+ --layout-self-center_-_align-self: center;
+ ;
+
+ --layout-self-end_-_-ms-align-self: flex-end;
+ --layout-self-end_-_-webkit-align-self: flex-end;
+ --layout-self-end_-_align-self: flex-end;
+ ;
+
+ --layout-self-stretch_-_-ms-align-self: stretch;
+ --layout-self-stretch_-_-webkit-align-self: stretch;
+ --layout-self-stretch_-_align-self: stretch;
+ ;
+
+ --layout-self-baseline_-_-ms-align-self: baseline;
+ --layout-self-baseline_-_-webkit-align-self: baseline;
+ --layout-self-baseline_-_align-self: baseline;
+ ;
+
+
+
+ --layout-start-aligned_-_-ms-flex-line-pack: start;
+ --layout-start-aligned_-_-ms-align-content: flex-start;
+ --layout-start-aligned_-_-webkit-align-content: flex-start;
+ --layout-start-aligned_-_align-content: flex-start;
+ ;
+
+ --layout-end-aligned_-_-ms-flex-line-pack: end;
+ --layout-end-aligned_-_-ms-align-content: flex-end;
+ --layout-end-aligned_-_-webkit-align-content: flex-end;
+ --layout-end-aligned_-_align-content: flex-end;
+ ;
+
+ --layout-center-aligned_-_-ms-flex-line-pack: center;
+ --layout-center-aligned_-_-ms-align-content: center;
+ --layout-center-aligned_-_-webkit-align-content: center;
+ --layout-center-aligned_-_align-content: center;
+ ;
+
+ --layout-between-aligned_-_-ms-flex-line-pack: justify;
+ --layout-between-aligned_-_-ms-align-content: space-between;
+ --layout-between-aligned_-_-webkit-align-content: space-between;
+ --layout-between-aligned_-_align-content: space-between;
+ ;
+
+ --layout-around-aligned_-_-ms-flex-line-pack: distribute;
+ --layout-around-aligned_-_-ms-align-content: space-around;
+ --layout-around-aligned_-_-webkit-align-content: space-around;
+ --layout-around-aligned_-_align-content: space-around;
+ ;
+
+
+
+ --layout-block_-_display: block;
+ ;
+
+ --layout-invisible_-_visibility: hidden !important;
+ ;
+
+ --layout-relative_-_position: relative;
+ ;
+
+ --layout-fit_-_position: absolute;
+ --layout-fit_-_top: 0;
+ --layout-fit_-_right: 0;
+ --layout-fit_-_bottom: 0;
+ --layout-fit_-_left: 0;
+ ;
+
+ --layout-scroll_-_-webkit-overflow-scrolling: touch;
+ --layout-scroll_-_overflow: auto;
+ ;
+
+ --layout-fullbleed_-_margin: 0;
+ --layout-fullbleed_-_height: 100vh;
+ ;
+
+
+
+ --layout-fixed-top_-_position: fixed;
+ --layout-fixed-top_-_top: 0;
+ --layout-fixed-top_-_left: 0;
+ --layout-fixed-top_-_right: 0;
+ ;
+
+ --layout-fixed-right_-_position: fixed;
+ --layout-fixed-right_-_top: 0;
+ --layout-fixed-right_-_right: 0;
+ --layout-fixed-right_-_bottom: 0;
+ ;
+
+ --layout-fixed-bottom_-_position: fixed;
+ --layout-fixed-bottom_-_right: 0;
+ --layout-fixed-bottom_-_bottom: 0;
+ --layout-fixed-bottom_-_left: 0;
+ ;
+
+ --layout-fixed-left_-_position: fixed;
+ --layout-fixed-left_-_top: 0;
+ --layout-fixed-left_-_bottom: 0;
+ --layout-fixed-left_-_left: 0;
+ ;
+}
diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js
index 245dd60..7813c4c 100644
--- a/frontend/src/lang/en.js
+++ b/frontend/src/lang/en.js
@@ -5,7 +5,6 @@ export default {
help: 'Help',
projectAddress: 'Project address',
giftRecordOfficial: 'Official Super Chat record',
- giftRecord: 'Super Chat record'
},
home: {
roomIdEmpty: "Room ID can't be empty",
@@ -53,6 +52,7 @@ export default {
avatarSize: 'Avatar size',
userNames: 'User names',
+ showUserNames: 'Show user names',
font: 'Font',
fontSize: 'Font size',
lineHeight: 'Line height (0 for default)',
diff --git a/frontend/src/lang/ja.js b/frontend/src/lang/ja.js
index 7d017a0..fcadfb6 100644
--- a/frontend/src/lang/ja.js
+++ b/frontend/src/lang/ja.js
@@ -5,7 +5,6 @@ export default {
help: 'ヘルプ',
projectAddress: 'プロジェクトアドレス',
giftRecordOfficial: '公式スーパーチャット記録',
- giftRecord: 'スーパーチャット記録'
},
home: {
roomIdEmpty: 'ルームのIDを空白にすることはできません',
@@ -53,6 +52,7 @@ export default {
avatarSize: 'アイコンのサイズ',
userNames: 'ユーザー名',
+ showUserNames: 'ユーザー名を表示する',
font: 'フォント',
fontSize: 'フォントサイズ',
lineHeight: '行の高さ(0はデフォルト)',
diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js
index 6bc6b4c..819d87b 100644
--- a/frontend/src/lang/zh.js
+++ b/frontend/src/lang/zh.js
@@ -5,7 +5,6 @@ export default {
help: '帮助',
projectAddress: '项目地址',
giftRecordOfficial: '官方打赏记录',
- giftRecord: '打赏记录'
},
home: {
roomIdEmpty: '房间ID不能为空',
@@ -53,6 +52,7 @@ export default {
avatarSize: '头像尺寸',
userNames: '用户名',
+ showUserNames: '显示用户名',
font: '字体',
fontSize: '字体尺寸',
lineHeight: '行高(0为默认)',
diff --git a/frontend/src/layout/Sidebar.vue b/frontend/src/layout/Sidebar.vue
index 2c4a45d..d87021c 100644
--- a/frontend/src/layout/Sidebar.vue
+++ b/frontend/src/layout/Sidebar.vue
@@ -26,11 +26,6 @@
{{$t('sidebar.giftRecordOfficial')}}
-
-
- {{$t('sidebar.giftRecord')}}
-
-
Language
diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue
index 5f37195..153c22a 100644
--- a/frontend/src/layout/index.vue
+++ b/frontend/src/layout/index.vue
@@ -9,7 +9,7 @@
- v1.4.4
+ v1.4.5
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index d5a6932..a753a4f 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -27,8 +27,8 @@ export function formatCurrency (price) {
}).format(price)
}
-export function getTimeTextMinSec (date) {
+export function getTimeTextHourMin (date) {
+ let hour = date.getHours()
let min = ('00' + date.getMinutes()).slice(-2)
- let sec = ('00' + date.getSeconds()).slice(-2)
- return `${min}:${sec}`
+ return `${hour}:${min}`
}
diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue
index 23c5791..8ce59d3 100644
--- a/frontend/src/views/Home.vue
+++ b/frontend/src/views/Home.vue
@@ -53,7 +53,7 @@
-
+
@@ -66,7 +66,7 @@
-
+
{{$t('home.copy')}}
@@ -90,7 +90,8 @@ export default {
data() {
return {
serverConfig: {
- enableTranslate: true
+ enableTranslate: true,
+ loaderUrl: ''
},
form: {
roomId: parseInt(window.localStorage.roomId || '1'),
@@ -107,6 +108,17 @@ export default {
delete query.roomId
let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId}, query})
return `${window.location.protocol}//${window.location.host}${resolved.href}`
+ },
+ obsRoomUrl() {
+ if (this.roomUrl === '') {
+ return ''
+ }
+ if (this.serverConfig.loaderUrl === '') {
+ return this.roomUrl
+ }
+ let url = new URL(this.serverConfig.loaderUrl)
+ url.searchParams.append('url', this.roomUrl)
+ return url.href
}
},
watch: {
@@ -121,7 +133,7 @@ export default {
methods: {
async updateServerConfig() {
try {
- this.serverConfig = (await axios.get(`/server_info`)).data.config
+ this.serverConfig = (await axios.get('/api/server_info')).data.config
} catch (e) {
this.$message.error('Failed to fetch server information: ' + e)
}
diff --git a/frontend/src/views/Room.vue b/frontend/src/views/Room.vue
index b5204db..f31c7d7 100644
--- a/frontend/src/views/Room.vue
+++ b/frontend/src/views/Room.vue
@@ -84,7 +84,7 @@ export default {
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
// 开发时使用localhost:12450
const host = process.env.NODE_ENV === 'development' ? 'localhost:12450' : window.location.host
- const url = `${protocol}://${host}/chat`
+ const url = `${protocol}://${host}/api/chat`
this.websocket = new WebSocket(url)
this.websocket.onopen = this.onWsOpen
this.websocket.onclose = this.onWsClose
@@ -188,8 +188,8 @@ export default {
avatarUrl: data.avatarUrl,
time: new Date(data.timestamp * 1000),
authorName: data.authorName,
- title: 'NEW MEMBER!',
- content: `Welcome ${data.authorName}!`
+ privilegeType: data.privilegeType,
+ title: 'New member'
}
break
case COMMAND_ADD_SUPER_CHAT:
diff --git a/frontend/src/views/StyleGenerator/index.vue b/frontend/src/views/StyleGenerator/index.vue
index 1973a60..54d029b 100644
--- a/frontend/src/views/StyleGenerator/index.vue
+++ b/frontend/src/views/StyleGenerator/index.vue
@@ -22,6 +22,9 @@
{{$t('stylegen.userNames')}}
+
+
+
@@ -219,15 +222,15 @@ let textMessageTemplate = {
repeated: 1,
translation: ''
}
-let legacyPaidMessageTemplate = {
+let membershipItemTemplate = {
id: 0,
addTime: time,
type: constants.MESSAGE_TYPE_MEMBER,
avatarUrl: 'https://static.hdslb.com/images/member/noface.gif',
time: time,
authorName: '',
- title: 'NEW MEMBER!',
- content: ''
+ privilegeType: 3,
+ title: 'New member'
}
let paidMessageTemplate = {
id: 0,
@@ -266,15 +269,14 @@ const EXAMPLE_MESSAGES = [
content: 'kksk'
},
{
- ...legacyPaidMessageTemplate,
+ ...membershipItemTemplate,
id: (nextId++).toString(),
- authorName: '进击的冰糖',
- content: 'Welcome 进击的冰糖!'
+ authorName: '艾米亚official'
},
{
...paidMessageTemplate,
id: (nextId++).toString(),
- authorName: '无火的残渣',
+ authorName: '愛里紗メイプル',
price: 66600,
content: 'Sent 小电视飞船x100'
},
@@ -288,7 +290,7 @@ const EXAMPLE_MESSAGES = [
{
...paidMessageTemplate,
id: (nextId++).toString(),
- authorName: '夏色祭保護協会会長',
+ authorName: 'AstralisUP',
price: 30,
content: '言いたいことがあるんだよ!'
}
diff --git a/frontend/src/views/StyleGenerator/stylegen.js b/frontend/src/views/StyleGenerator/stylegen.js
index dff4cb1..6155afd 100644
--- a/frontend/src/views/StyleGenerator/stylegen.js
+++ b/frontend/src/views/StyleGenerator/stylegen.js
@@ -9,6 +9,7 @@ export const DEFAULT_CONFIG = {
showAvatars: true,
avatarSize: 24,
+ showUserNames: true,
userNameFont: 'Changa One',
userNameFontSize: 20,
userNameLineHeight: 0,
@@ -16,7 +17,7 @@ export const DEFAULT_CONFIG = {
ownerUserNameColor: '#ffd600',
moderatorUserNameColor: '#5e84f1',
memberUserNameColor: '#0f9d58',
- showBadges: false,
+ showBadges: true,
showColon: true,
messageFont: 'Imprima',
@@ -107,13 +108,13 @@ yt-live-chat-author-chip #author-name {
/* Outlines */
yt-live-chat-renderer * {
${getShowOutlinesStyle(config)}
- font-family: "${config.messageFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.messageFont)}"${FALLBACK_FONTS};
font-size: ${config.messageFontSize}px !important;
- line-height: ${config.messageLineHeight}px !important;
+ line-height: ${config.messageLineHeight || config.messageFontSize}px !important;
}
yt-live-chat-text-message-renderer #content,
-yt-live-chat-legacy-paid-message-renderer #content {
+yt-live-chat-membership-item-renderer #content {
overflow: initial !important;
}
@@ -133,12 +134,7 @@ yt-live-chat-message-input-renderer {
}
/* Reduce side padding. */
-yt-live-chat-text-message-renderer,
-yt-live-chat-legacy-paid-message-renderer {
- ${getPaddingStyle(config)}
-}
-
-yt-live-chat-paid-message-renderer #header {
+yt-live-chat-text-message-renderer {
${getPaddingStyle(config)}
}
@@ -147,8 +143,8 @@ yt-live-chat-text-message-renderer #author-photo,
yt-live-chat-text-message-renderer #author-photo img,
yt-live-chat-paid-message-renderer #author-photo,
yt-live-chat-paid-message-renderer #author-photo img,
-yt-live-chat-legacy-paid-message-renderer #author-photo,
-yt-live-chat-legacy-paid-message-renderer #author-photo img {
+yt-live-chat-membership-item-renderer #author-photo,
+yt-live-chat-membership-item-renderer #author-photo img {
${config.showAvatars ? '' : 'display: none !important;'}
width: ${config.avatarSize}px !important;
height: ${config.avatarSize}px !important;
@@ -166,7 +162,7 @@ yt-live-chat-text-message-renderer #chat-badges {
yt-live-chat-text-message-renderer #timestamp {
display: ${config.showTime ? 'inline' : 'none'} !important;
${config.timeColor ? `color: ${config.timeColor} !important;` : ''}
- font-family: "${config.timeFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.timeFont)}"${FALLBACK_FONTS};
font-size: ${config.timeFontSize}px !important;
line-height: ${config.timeLineHeight || config.timeFontSize}px !important;
}
@@ -189,8 +185,9 @@ yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="memb
/* Channel names. */
yt-live-chat-text-message-renderer #author-name {
+ ${config.showUserNames ? '' : 'display: none !important;'}
${config.userNameColor ? `color: ${config.userNameColor} !important;` : ''}
- font-family: "${config.userNameFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.userNameFont)}"${FALLBACK_FONTS};
font-size: ${config.userNameFontSize}px !important;
line-height: ${config.userNameLineHeight || config.userNameFontSize}px !important;
}
@@ -201,7 +198,7 @@ ${getShowColonStyle(config)}
yt-live-chat-text-message-renderer #message,
yt-live-chat-text-message-renderer #message * {
${config.messageColor ? `color: ${config.messageColor} !important;` : ''}
- font-family: "${config.messageFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.messageFont)}"${FALLBACK_FONTS};
font-size: ${config.messageFontSize}px !important;
line-height: ${config.messageLineHeight || config.messageFontSize}px !important;
}
@@ -213,20 +210,20 @@ ${!config.messageOnNewLine ? '' : `yt-live-chat-text-message-renderer #message {
/* SuperChat/Fan Funding Messages. */
yt-live-chat-paid-message-renderer #author-name,
yt-live-chat-paid-message-renderer #author-name *,
-yt-live-chat-legacy-paid-message-renderer #event-text,
-yt-live-chat-legacy-paid-message-renderer #event-text * {
+yt-live-chat-membership-item-renderer #header-content-inner-column,
+yt-live-chat-membership-item-renderer #header-content-inner-column * {
${config.firstLineColor ? `color: ${config.firstLineColor} !important;` : ''}
- font-family: "${config.firstLineFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.firstLineFont)}"${FALLBACK_FONTS};
font-size: ${config.firstLineFontSize}px !important;
line-height: ${config.firstLineLineHeight || config.firstLineFontSize}px !important;
}
yt-live-chat-paid-message-renderer #purchase-amount,
yt-live-chat-paid-message-renderer #purchase-amount *,
-yt-live-chat-legacy-paid-message-renderer #detail-text,
-yt-live-chat-legacy-paid-message-renderer #detail-text * {
+yt-live-chat-membership-item-renderer #header-subtext,
+yt-live-chat-membership-item-renderer #header-subtext * {
${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''}
- font-family: "${config.secondLineFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.secondLineFont)}"${FALLBACK_FONTS};
font-size: ${config.secondLineFontSize}px !important;
line-height: ${config.secondLineLineHeight || config.secondLineFontSize}px !important;
}
@@ -234,7 +231,7 @@ yt-live-chat-legacy-paid-message-renderer #detail-text * {
yt-live-chat-paid-message-renderer #content,
yt-live-chat-paid-message-renderer #content * {
${config.scContentColor ? `color: ${config.scContentColor} !important;` : ''}
- font-family: "${config.scContentFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.scContentFont)}"${FALLBACK_FONTS};
font-size: ${config.scContentFontSize}px !important;
line-height: ${config.scContentLineHeight || config.scContentFontSize}px !important;
}
@@ -243,17 +240,18 @@ yt-live-chat-paid-message-renderer {
margin: 4px 0 !important;
}
-yt-live-chat-legacy-paid-message-renderer #card {
+yt-live-chat-membership-item-renderer #card,
+yt-live-chat-membership-item-renderer #header {
${getShowNewMemberBgStyle(config)}
}
yt-live-chat-text-message-renderer a,
-yt-live-chat-legacy-paid-message-renderer a {
+yt-live-chat-membership-item-renderer a {
text-decoration: none !important;
}
yt-live-chat-text-message-renderer[is-deleted],
-yt-live-chat-legacy-paid-message-renderer[is-deleted] {
+yt-live-chat-membership-item-renderer[is-deleted] {
display: none !important;
}
@@ -275,7 +273,7 @@ yt-live-chat-ticker-paid-message-item-renderer *,
yt-live-chat-ticker-sponsor-item-renderer,
yt-live-chat-ticker-sponsor-item-renderer * {
${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''}
- font-family: "${config.secondLineFont}"${FALLBACK_FONTS};
+ font-family: "${cssEscapeStr(config.secondLineFont)}"${FALLBACK_FONTS};
}
yt-live-chat-mode-change-message-renderer,
@@ -339,6 +337,31 @@ function getShowOutlinesStyle (config) {
return `text-shadow: ${shadow.join(', ')};`
}
+function cssEscapeStr (str) {
+ let res = []
+ for (let char of str) {
+ res.push(cssEscapeChar(char))
+ }
+ return res.join('')
+}
+
+function cssEscapeChar (char) {
+ if (!needEscapeChar(char)) {
+ return char
+ }
+ let hexCode = char.codePointAt(0).toString(16)
+ // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
+ return `\\${hexCode} `
+}
+
+function needEscapeChar (char) {
+ let code = char.codePointAt(0)
+ if (0x20 <= code && code <= 0x7E) {
+ return char === '"'
+ }
+ return true
+}
+
function getPaddingStyle (config) {
return `padding-left: ${config.useBarsInsteadOfBg ? 20 : 4}px !important;
padding-right: 4px !important;`
@@ -399,7 +422,8 @@ ${keyframes.join('\n')}
}
yt-live-chat-text-message-renderer,
-yt-live-chat-legacy-paid-message-renderer {
+yt-live-chat-membership-item-renderer,
+yt-live-chat-paid-message-renderer {
animation: anim ${totalTime}ms;
animation-fill-mode: both;
}`
diff --git a/log/.gitkeep b/log/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/main.py b/main.py
index ad7db0e..d6d2641 100644
--- a/main.py
+++ b/main.py
@@ -2,6 +2,7 @@
import argparse
import logging
+import logging.handlers
import os
import webbrowser
@@ -18,15 +19,19 @@ import update
logger = logging.getLogger(__name__)
-WEB_ROOT = os.path.join(os.path.dirname(__file__), 'frontend', 'dist')
+BASE_PATH = os.path.dirname(os.path.realpath(__file__))
+WEB_ROOT = os.path.join(BASE_PATH, 'frontend', 'dist')
+LOG_FILE_NAME = os.path.join(BASE_PATH, 'log', 'blivechat.log')
routes = [
+ (r'/api/server_info', api.main.ServerInfoHandler),
+ (r'/api/chat', api.chat.ChatHandler),
+
+ # TODO 兼容旧版,下版本移除
(r'/server_info', api.main.ServerInfoHandler),
(r'/chat', api.chat.ChatHandler),
- (r'/((css|fonts|img|js|static)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
- (r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
- (r'/.*', api.main.MainHandler, {'path': WEB_ROOT})
+ (r'/(.*)', api.main.MainHandler, {'path': WEB_ROOT, 'default_filename': 'index.html'})
]
@@ -45,7 +50,7 @@ def main():
def parse_args():
- parser = argparse.ArgumentParser(description='用于OBS的仿YouTube风格的bilibili直播聊天层')
+ parser = argparse.ArgumentParser(description='用于OBS的仿YouTube风格的bilibili直播评论栏')
parser.add_argument('--host', help='服务器host,默认为127.0.0.1', default='127.0.0.1')
parser.add_argument('--port', help='服务器端口,默认为12450', type=int, default=12450)
parser.add_argument('--debug', help='调试模式', action='store_true')
@@ -53,11 +58,16 @@ def parse_args():
def init_logging(debug):
+ stream_handler = logging.StreamHandler()
+ file_handler = logging.handlers.TimedRotatingFileHandler(
+ LOG_FILE_NAME, encoding='utf-8', when='midnight', backupCount=7, delay=True
+ )
logging.basicConfig(
format='{asctime} {levelname} [{name}]: {message}',
datefmt='%Y-%m-%d %H:%M:%S',
style='{',
- level=logging.INFO if not debug else logging.DEBUG
+ level=logging.INFO if not debug else logging.DEBUG,
+ handlers=[stream_handler, file_handler]
)
@@ -68,8 +78,13 @@ def run_server(host, port, debug):
debug=debug,
autoreload=False
)
+ cfg = config.get_config()
try:
- app.listen(port, host)
+ app.listen(
+ port,
+ host,
+ xheaders=cfg.tornado_xheaders
+ )
except OSError:
logger.warning('Address is used %s:%d', host, port)
return
diff --git a/models/translate.py b/models/translate.py
index 205e0d0..19d340b 100644
--- a/models/translate.py
+++ b/models/translate.py
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
NO_TRANSLATE_TEXTS = {
'草', '草草', '草草草', '草生', '大草原', '上手', '上手上手', '理解', '理解理解', '天才', '天才天才',
- '强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故'
+ '强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故', '清楚', '清楚清楚'
}
_main_event_loop = asyncio.get_event_loop()
diff --git a/update.py b/update.py
index c323847..6d66c6f 100644
--- a/update.py
+++ b/update.py
@@ -4,7 +4,7 @@ import asyncio
import aiohttp
-VERSION = 'v1.4.4'
+VERSION = 'v1.4.5'
def check_update():