Skip to content

Commit

Permalink
PUSH8 (#7)
Browse files Browse the repository at this point in the history
* PUSH 7 modify 2

* PUSH 8 modify 1

* PUSH 8 modify 2

* PUSH 8 modify 3

* PUSH 8 modify 4

* PUSH 8 modify 5

* PUSH 8 modify 6

* PUSH 8 modify 7

* PUSH 8 modify 8

* Read me update

* PUSH8 modify 9
MeowDWing authored Sep 7, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 7ee0088 commit 4509a08
Showing 8 changed files with 637 additions and 280 deletions.
48 changes: 29 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,53 +2,57 @@

--------

**请在每次拿到新版本后输入u进入更新说明查看更新内容**

**本项目有如下功能及特点:**

* **读取弹幕本直播间有粉丝牌的粉丝弹幕**


* **设置屏蔽词**


* 参照b站up资深小狐狸直播时的需要设计
* <font color=#00FFFF>参照b站up资深小狐狸直播时的需要设计
* 自动追赶机制:帮助你直击最新弹幕(PUSH4 发布后)
* 绝对的排外政策:可调节读取所需要的牌子等级(默认1级),只要不是自己直播间的牌子多少级都不读(PUSH4 发布后)
* 绝对的排外政策:可调节读取所需要的牌子等级(默认1级)
* 极端的亲外决议:当读取所需等级设为0时,会读取所有弹幕(只要他能读出来)(PUSH8发布后)
* 没有阀门制度:跳过弹幕时不看等级,一视同仁(PUSH5 发布后)
* 预设屏蔽词:符合狐宝直播需要,包含 。/ 赞 和所有个位数字(PUSH5 发布后)
* 当消息仅包含中英文常用符号时,拒绝加入消息队列(PUSH5发布后)
* 正则表达式匹配(PUSH7 发布后)
* 正则表达式匹配(PUSH7 发布后)</font>


* <font color=#FFFF00>基于pyttsx3(PUSH6 发布后)</font>

* 基于pyttsx3

* <font color=coffee>采用多进程编写(PUSH8 发布后)</font>

* 注意:python版经过作者更详细的测试,会更加稳定;release版是pyinstaller打包完成的,只测试了可运行性

* **注意:python版经过作者更详细的测试,会更加稳定;release版是pyinstaller打包完成的,只测试了可运行性**

* 打开项目后,可在对应直播间依次输入以下内容进行测试(注意直播间粉丝牌等级,没有粉丝牌或等级不足会拒绝加入队列):
* 好好好 > reader界面应出现:好好好 准备读取

* <font color=#00FACB>打开项目后,可在对应直播间依次输入以下内容进行测试(注意直播间粉丝牌等级,没有粉丝牌或等级不足会拒绝加入队列):
* 好好好 > 界面应出现:好好好 准备读取
* 好好好 > 本次好好好不会读出来,因为相邻弹幕一致会拒绝读取
* 。。。 > reader界面应出现:检测到。。。中仅包含符号,拒绝加入队列
* 狐宝新婚快乐 > reader界面应出现:狐宝新婚快乐 准备读取
* 好好好 > reader界面应出现:好好好 准备读取
* 。。。 > 界面应出现:检测到。。。中仅包含符号,拒绝加入队列
* 狐宝新婚快乐 > 界面应出现:狐宝新婚快乐 准备读取
* 好好好 > 界面应出现:好好好 准备读取
* 赞 > 本次赞因为在屏蔽词中,所以不会加入队列也不会读出来
* 以上测试全部通过后,即可说明程序基本功能正常运行,如果有任何错误,请联系作者。
* 错误信息可以如下形式保留:
* 1,在项目文件夹右键打开cmd/windows terminal
* 2,在cmd中,python请运行python main.py / release运行main.exe
* 3,配置完成后,在主界面输入b启动程序,并关闭自动打开的reader界面
* 4,以打开main同样的方式打开reader.py / reader.exe
* 5,复现错误并将错误信息发送给作者,我会十分感谢您的错误报告

* 3,配置完成后,在主界面输入b启动程序
* 4,复现错误并将错误信息发送给作者,我会十分感谢您的错误报告
</font>

****
**使用方法:**
**使用方法:(需要登录b站账号,请仔细阅读登录界面说明)**
* py文件(推荐)(需要库:bilibili-api-python、pyttsx3)
* 你可以通过`pip install bilibili-api-python pyttsx3` 直接安装
* 直接运行main.py即可根据提示操作
* 由于未知bug,需要在启动后手动启动reader.py

* release
* 直接运行main.exe即可根据提示操作,如果reader没有自动打开,可以手动启动
* 直接运行main.exe即可根据提示操作


*****
@@ -67,6 +71,12 @@
* rid代表接入直播间的直播间id, minlevel表示读取弹幕所需的最小本直播间粉丝牌等级
* 由于api问题(没有牌子的直播徽章返回值是None而不是0),所以没有专门做所有人可阅读的设置,如有需要联系作者(github或b站均可)

* INITIAL
* 本文件标记是否初始化并记录登录信息,**记录内容均为敏感内容且明文保存,请注意妥善保管**

* login
* 本文件为自动登录标记文件,为空


*****
**机制说明**
@@ -90,7 +100,7 @@
* A:在reader界面,每个机制的每次运行都会发送运行消息,该界面是重要的调试界面,如果你无意调试,可以无视这个界面的输出。


* Q:为什么没有做界面?为什么需要打开两个窗口才能运行?
* Q:为什么没有做界面?
* A:很简单,因为作者不会,没学过。


2 changes: 1 addition & 1 deletion initial.py
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ def _initial():
'$ 更多操作请参看README.MD文件(可以直接以文本形式打开)\n'
'。\n'
'赞\n'
"$ '-'(减号)开头的会作为匹配词屏蔽。"
"$ '-'(减号)开头的会作为匹配词屏蔽。\n"
"$ 系统会屏蔽所有包含匹配词的弹幕,请谨慎选择\n"
'-红包\n'
)
392 changes: 392 additions & 0 deletions interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,392 @@
import time

import bilibili_api

import iosetting as ios
import os
import re
import main
import multiprocessing
import initial
import liveget as lg
import reader as rd
from bilibili_api.login import login_with_password, login_with_sms, send_sms, PhoneNumber
from bilibili_api import login, sync, settings, exceptions
from bilibili_api.credential import Credential


def interface(proj_name: str, set_dict: dict, version: str, location: str, pflag: bool = False, eflag: bool = False):

# 设置区
items = len(set_dict.keys())
line_int = int(items/3)
line_rest = items % 3
set_lines = []
for key, value in set_dict.items():
set_lines.append(f'{key.upper()}({key}).{value}')

llen = len(set_lines)
cpj_n = characters_num(proj_name)
cver_n = characters_num(version)
cloc_n = characters_num(location)

# 显示区
print(f"|*{'=' * 76}*|")
print(f"|*|{proj_name.center(74-cpj_n)}|*|")
print(f"|*| {location.ljust(33-cloc_n)}{version.rjust(33-cver_n)} |*|")
print(f"|*{'=' * 76}*|")
print(f"|*|{' ' * 74}|*|")

tmp = 0
if line_int != 0:
for i in range(line_int):
l1 = characters_num(set_lines[tmp])
l2 = characters_num(set_lines[tmp+1])
l3 = characters_num(set_lines[tmp+2])
print(f"|*| "
f"{set_lines[tmp].ljust(22-l1)}"
f"{set_lines[tmp+1].ljust(22-l2)}"
f"{set_lines[tmp+2].ljust(22-l3)}"
f"|*|")
print(f"|*|{' ' * 74}|*|")
tmp += 3
if line_rest != 0:
if line_rest == 1:
print(f"|*| {set_lines[tmp].ljust(66-characters_num(set_lines[tmp]))}|*|")
else:
ll2 = characters_num(set_lines[tmp])
ll1 = characters_num(set_lines[tmp+1])
print(f"|*| "
f"{set_lines[tmp].ljust(22 - ll2)}"
f"{set_lines[tmp+1].ljust(44 - ll1)}"
f"|*|")
print(f"|*|{' ' * 74}|*|")
if eflag:
if location == 'main':
print(f"|*| {'S(s).设置'.ljust(44-2)}{'E(e).退出'.ljust(22-2)}|*|")
print(f"|*|{' ' * 74}|*|")
elif pflag:
print(f"|*| {'P(p).上一级'.ljust(44-3)}{'E(e).退出'.ljust(22-2)}|*|")
print(f"|*|{' ' * 74}|*|")
print(f"|*{'=' * 76}*|")

# print('|*===================================================================*|\n'
# '|*| DANMAKU READER |*|\n'
# f'|*| main -> 查看 {__VERSION__} |*|\n'
# '|*===================================================================*|\n'
# '|*| |*|\n'
# '|*| B(b).禁读词列表 S(s).设置文件 |*|\n'
# '|*| |*|\n'
# '|*| |*|\n'
# '|*| P(p).返回上一级 E(e):退出 |*|\n'
# '|*| |*|\n'
# '|*===================================================================*|'
# )


def characters_num(rev: str) -> int:
_m = re.findall("\\\\x[0-9a-f][0-9a-f]", str(rev.encode('utf-8')))
_l = len(_m)
return int(_l/3)


def update_content(version: str):
with open(f'{version}.md', mode='r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
ios.print_details(line, tag='CTRL')


class MFunc:

@staticmethod
def reset():

ios.print_details('执行指令将重置所有文件(如屏蔽词,登录信息等),你确定要继续吗(y/n)?', tag='WARNING', end='')
key = input("").strip().upper()
if key == 'Y':
file_clearer('./files')
if os.path.exists('ban_word.txt'):
os.remove('ban_word.txt')
initial.initial()
else:
return

@staticmethod
def begin():

__global_queue = multiprocessing.Queue(233)
print('正在读取房间号...')
rid = 34162
with open('./files/settings.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
line = line.strip().split('=')
if line[0] == 'rid':
rid = int(line[1])

print('正在初始化弹幕获取器...')
process_receiver = multiprocessing.Process(target=receiver, args=(__global_queue, rid,))

print("正在初始化阅读器...")
process_reader = multiprocessing.Process(target=reader, args=(__global_queue,))

print("正在启动弹幕获取器与读取器...")
process_receiver.start()
process_reader.start()

process_receiver.join()
process_reader.join()

@staticmethod
def check():
while True:
os.system('cls')
interface(
proj_name=main.__PROJ_NAME__,
location='main -> 查看',
version=main.__VERSION__,
set_dict={
'b': '禁读词列表',
's': '设置'
},
pflag=True,
eflag=True
)
# print('|*===================================================================*|\n'
# '|*| DANMAKU READER |*|\n'
# f'|*| main -> 查看 {__VERSION__} |*|\n'
# '|*===================================================================*|\n'
# '|*| |*|\n'
# '|*| B(b).禁读词列表 S(s).设置文件 |*|\n'
# '|*| |*|\n'
# '|*| |*|\n'
# '|*| P(p).返回上一级 E(e):退出 |*|\n'
# '|*| |*|\n'
# '|*===================================================================*|'
# )
print('>>>', end='')
get = input()
get = get.strip()
label = get[0].upper()
match label:
case 'B':
os.system('ban_word.txt')
case 'S':
cwd = os.getcwd()
os.chdir(cwd + '/files')
os.system('settings.txt')
os.chdir(cwd)
case 'P':
return
case 'E':
exit()
case _:
print(f'{label}没有在列表中')

@staticmethod
def login():
credentials: Credential = Credential()
sign = pw = 'False'
settings.geetest_auto_open = True
while True:
os.system('cls')
interface(
proj_name=main.__PROJ_NAME__,
version=main.__VERSION__,
location='main->登录',
set_dict={
'a': '验证码',
'b': '账号密码',
'c': '二维码'
},
pflag=True, eflag=True,
)
ios.print_details('选择账号密码登陆后,即自动保存至本地,几天内即无需再次登录,之后若无法使用,请再次登录', tag='TIPS', head='TIPS')
ios.print_details('本程序所有输入与保存均为明文形式,切勿泄露,并在确保网络安全的情况下(私人或可信的公共网络下)使用', tag='UP', head='TIPS')
get = input('>>>').strip().upper()
match get:
case 'A': credentials = LoginFunc.login_by_sms()
case 'B': credentials, sign, pw = LoginFunc.login_by_pw()
case 'C': credentials = LoginFunc.qrcode()
case 'P': return
case 'E': exit(0)

if credentials is not None:
if sign != 'False' and pw != 'False':
with open('./files/INITIAL', mode='w', encoding='utf-8') as f:
lines = [
'本文件行对应,请勿以任何方式更改本文件\n',
f'sign={sign}\n',
f'pw={pw}\n',
f'sessdate={credentials.sessdata}\n',
f'bili_jct={credentials.bili_jct}\n',
f'buvid3={credentials.buvid3}\n',
f'ac_time_value={credentials.ac_time_value}\n'
]
f.writelines(lines)
ios.print_simple('保存完毕', base='CTRL')
time.sleep(1)
return

else:
with open('./files/INITIAL', mode='r', encoding='utf-8') as f:
lines = f.readlines()
if len(lines) > 0:
if len(lines[1]) > 7 and len(lines[2]) > 5:
sign = lines[1][5:-1]
pw = lines[2][3:-1]
else:
sign = ''
pw = ''

with open('./files/INITIAL', mode='w', encoding='utf-8') as f:
lines = [
'本文件行对应,除非理解程序逻辑,否则请勿以任何方式更改本文件\n',
f'sign={sign}\n',
f'pw={pw}\n',
f'sessdate={credentials.sessdata}\n',
f'bili_jct={credentials.bili_jct}\n',
f'buvid3={credentials.buvid3}\n',
f'ac_time_value={credentials.ac_time_value}\n'
]
f.writelines(lines)
ios.print_simple('保存完毕', base='CTRL')
time.sleep(1)

@staticmethod
def updatec():
update_content(main.__VERSION__)
input("很好,我知道了!(Enter退出)")

@staticmethod
def setting():

if os.path.exists('./files/login'):
login_flag = 'Y'
else:
login_flag = 'N'

while True:
os.system('cls')
interface(
proj_name=main.__PROJ_NAME__,
version=main.__VERSION__,
location='main->设置',
set_dict={
'a': f'自动登录 {login_flag}',
},
eflag=True, pflag=True
)

get = input('>>>').strip().upper()
match get:
case 'A': login_flag = SetFunc.auto_login(login_flag)
case 'P': return
case 'E': exit()


class LoginFunc:

@staticmethod
def login_by_pw():
username = input("请输入手机号/邮箱:")
password = input("请输入密码:")
print("正在登录。")
try:
c = login_with_password(username, password)
except exceptions.LoginError as el:
c = None
ios.print_details(el.msg, tag='WRONG', head='WRONG', prefix='LOGIN')
time.sleep(1)
return None, 'False', 'False'
if isinstance(c, login.Check):
# 还需验证
print("需要进行验证。请考虑使用验证码登录")
return None, username, password
else:
credential = c
with open('./files/login', mode='w'):
pass
return credential, username, password

@staticmethod
def login_by_sms():
phone = input("请输入手机号:")
print("正在登录。")
send_sms(PhoneNumber(phone, country="+86")) # 默认设置地区为中国大陆
code = input("请输入验证码:")
c = login_with_sms(PhoneNumber(phone, country="+86"), code)
if isinstance(c, login.Check):
# 还需验证
print("需要进行验证。请考虑使用二维码登录")
return None
else:
credential = c
return credential

@staticmethod
def qrcode():
print("请登录:")
credential = login.login_with_qrcode_term() # 在终端扫描二维码登录
# credential = login.login_with_qrcode() # 使用窗口显示二维码登录
try:
credential.raise_for_no_bili_jct() # 判断是否成功
credential.raise_for_no_sessdata() # 判断是否成功
return credential
except:
print("登陆失败。。。")
time.sleep(3)
return None




class SetFunc:
@staticmethod
def auto_login(login_flag):
if login_flag == 'Y':
login_flag = 'N'
pathnow = os.getcwd()
os.remove('files/login')
else:
with open('./files/login', mode='w'):
login_flag = 'Y'

return login_flag



def receiver(_g_queue: multiprocessing.Queue, rid: int = 34162):
x = lg.LiveInfoGet(rid=rid, g_queue=_g_queue)
x.living_on()


def reader(_queue):
read = rd.Reader(_queue)
read.reader()


def file_clearer(path):
if os.path.exists(path):
archive = os.listdir(path)
for file in archive:
os.remove(path + f'/{file}')


if __name__ == '__main__':
interface(
proj_name='DanmakuReader',
version=main.__VERSION__,
set_dict={
'a': '初始化',
'b': '启动',
'c': '查看',
'd': '22',
'e': '33'
},
location='查看',
eflag=True,
pflag=True
)

50 changes: 41 additions & 9 deletions iosetting.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum

_iosetting__tag_dict = {
'CAPTAIN': '\033[94m', # Blue bright
'CAPTAIN_BUY_3': '\033[38;2;0;191;255m', # Deep Sky Blue
@@ -27,8 +29,30 @@
}


def print_set(text: str, tag: str = 'NORMAL', debug_flag: bool = False,
head: str = None, prefix: str = None, log=False, special_color='FFFFFF', end='\n'):
def print_simple(text: str, base: str = 'NORMAL',
special_color='FFFFFF', end='\n'):

begin_str = ''
end_str = '\033[0m'

if base in _iosetting__tag_dict:
begin_str = _iosetting__tag_dict[base]

match base:
case 'SC' | 'SC_JPN': # customizing color
color_str = hex2dec_str(special_color)
begin_str = f'\033[38;2;{color_str}m'
case 'SPECIAL':
color_str = hex2dec_str(special_color)
begin_str = f'\033[38;2;{color_str}m'

print(begin_str, end='')
print(text, end='')
print(end_str, end=end)


def print_details(text: str, tag: str = 'NORMAL', debug_flag: bool = False,
head: str = None, prefix: str = None, log=False, special_color='FFFFFF', end='\n'):
begin_str = ''
end_str = '\033[0m'

@@ -59,10 +83,18 @@ def print_set(text: str, tag: str = 'NORMAL', debug_flag: bool = False,
# \033[xxm[HEAD->PREFIX]TEXT[SUFFIX]\033[m
# \033[91m[SYSTEM->REPLY MODULE]xxxxxxxxx\033[m

print(begin_str, end='')
print(text, end='')
print(end_str, end=end)
if log:
# new print format:
#
# bcCTRL head prefix func text suffix ecCTRL
# \033[xxm[HEAD:PREFIX->FUNC]TEXT -> suffix ecCTRL
# \033[91m[SYS:Rec->Queue]xxxxxxxxxxxxx -> auto\033[m
###

if not debug_flag:
print(begin_str, end='')
print(text, end='')
print(end_str, end=end)
if log or debug_flag:
log_file = open("./logging.txt", mode='a', encoding='utf-8')
log_file.write(text+end)
log_file.close()
@@ -79,9 +111,9 @@ def hex2dec_str(str16: str = '#FFFFFF') -> str:
if str16[0] == '#':
str16 = str16[1:]
if len(str16) > 6:
print_set('A wrong RGB color set', tag='WARNING')
print_details('A wrong RGB color set', tag='WARNING')
str16 = str16[0:6]
print_set(f'new slice is {str16}', tag='WARNING')
print_details(f'new slice is {str16}', tag='WARNING')
R_channel = '0x'+str16[0:2]
G_channel = '0x'+str16[2:4]
B_channel = '0x'+str16[4:]
@@ -90,7 +122,7 @@ def hex2dec_str(str16: str = '#FFFFFF') -> str:
decG = int(G_channel, 16)
decB = int(B_channel, 16)
except ValueError:
print_set('You set a WRONG RGB code, color has been reset to 0x000000', tag='ERROR')
print_details('You set a WRONG RGB code, color has been reset to 0x000000', tag='ERROR')
decR = decG = decB = 0
trans_str = str(decR)+';'+str(decG)+';'+str(decB)
return trans_str # str -> xx;xx;xx
99 changes: 68 additions & 31 deletions liveget.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import multiprocessing
import os
import time
from collections import deque
import random
import bilibili_api

import iosetting as ios
import bilibili_api
from bilibili_api import live, sync, user, exceptions
from bilibili_api import live, sync, user, credential


# Exception Zone
@@ -18,9 +21,9 @@ def __str__(self):

class LiveInfoGet:

def __init__(self, uid: int = -1, rid: int = -1, # id zone
up_name: str = '资深小狐狸', ctrl_name: str = '吾名喵喵之翼'
):
def __init__(self, g_queue: multiprocessing.Queue,
uid: int = -1, rid: int = -1, # id zone
up_name: str = '资深小狐狸', ctrl_name: str = '吾名喵喵之翼', debug_flag: bool = False):
"""
你可以输入房间号或者uid的任何一个,代码会自动获取另一个
@@ -35,6 +38,19 @@ def __init__(self, uid: int = -1, rid: int = -1, # id zone
self.user_id = uid
self.up_name = up_name
self.ctrl_name = ctrl_name
self.debug_flag = debug_flag

self.__PREFIX = 'Rec'
if os.path.exists('./files/login'):
sessdate, bili_jct, buvid3, ac_time_value = self.get_credentials()
self.credentials = credential.Credential(sessdata=sessdate, bili_jct=bili_jct, buvid3=buvid3,
ac_time_value=ac_time_value)
else:
self.credentials = None
self._queue = g_queue
self.local_queue = deque()
self.queue_flag = False
self.local_queue_len = len(self.local_queue)

if not os.path.exists('./files'):
os.mkdir('./files')
@@ -43,18 +59,11 @@ def __init__(self, uid: int = -1, rid: int = -1, # id zone

# dictionary & list initial zone
self.settings_dict = {}
self.read_any_lvl = False
self.get_settings()
# badge_dict
self.badge_dict = {0: 'Passer'}
self.badge_dict.update({
lvl: 'Fans' for lvl in range(1, 21)
})
self.badge_dict.update({
lvl: 'Captain' for lvl in range(21, 50)
})

if self.user_id > 0:
self.user_detail = user.User(uid=self.user_id)
self.user_detail = user.User(uid=self.user_id, credential=self.credentials)
self.user_info = sync(self.user_detail.get_live_info())
self.room_id = self.user_info['live_room']['roomid']
if self.room_id > 0:
@@ -68,7 +77,7 @@ def __init__(self, uid: int = -1, rid: int = -1, # id zone
else:
raise UserInfoError("User_id maybe wrong, please check again")

self.room_event_stream = live.LiveDanmaku(self.room_id)
self.room_event_stream = live.LiveDanmaku(self.room_id, credential=self.credentials)

def get_settings(self):
should_have = set()
@@ -84,52 +93,80 @@ def get_settings(self):

for i in should_have:
if i not in self.settings_dict.keys():
ios.print_set(f'{i}似乎设置出错了', tag='WARNING')
ios.print_details(f'{i}似乎设置出错了', tag='WARNING')
time.sleep(3)

if self.settings_dict['min_level'] == '0':
self.read_any_lvl = True

def get_credentials(self):
with open('./files/INITIAL', mode='r', encoding='utf-8') as f:
lines = f.readlines()
if len(lines) > 0:
s = lines[3][9:-1]
b = lines[4][9:-1]
b3 = lines[5][7:-1]
if b3 == 'None':
b3 = None
a = lines[6][14:-1]
else:
s = b = b3 = a = None
return s, b, b3, a

def living_on(self):

@self.room_event_stream.on('DANMU_MSG')
async def on_danmaku(event): # event -> dictionary
if self.debug_flag:
ios.print_details('danmaku'+str(random.randint(0, 50000))+'='+str(event), debug_flag=True)
self.live_danmaku(event)
ios.print_set('弹幕开启', tag='SYSTEM')
ios.print_details('弹幕开启', tag='SYSTEM')

sync(self.room_event_stream.connect())


def live_danmaku(self, event: dict = None):

user_fans_lvl = 0
print_flag = 'NORMAL'
if_read = False

# main information processing Zone
live_info = event['data']['info'] # list[Unknown, Msg, user_info, fans_info, Unknown:]
danmaku_content = live_info[1]
user_main_info = live_info[2] # list[uid, Nickname, Unknown:]
nickname = user_main_info[1]
user_fans_info = live_info[3] # list[lvl, worn_badge, Unknown:]
if len(user_fans_info) != 0:

if len(user_fans_info) > 0:
if user_fans_info[1] == self.fans_badge:
print_flag = 'FANS'
user_fans_lvl = user_fans_info[0]
if user_fans_lvl > 20:
print_flag = 'CAPTAIN'
user_title = self.badge_dict[user_fans_lvl]
if len(danmaku_content) > 0 and user_fans_lvl >= int(self.settings_dict['min_level']):
with open('./files/danmaku.txt', mode='a', encoding='utf-8') as f:
f.write(danmaku_content+'\n')
if_read = True

if len(danmaku_content) > 0 and (self.read_any_lvl or if_read):
if not self._queue.full():
if self.local_queue_len != 0:
while True:
if not self._queue.full() and self.local_queue_len != 0:
c = self.local_queue.popleft()
self.local_queue_len -= 1
self._queue.put(c)
else:
break
else:
self._queue.put(danmaku_content)
else:
self.local_queue.append(danmaku_content)

match nickname:
case self.ctrl_name:
print_flag = 'CTRL'
case self.up_name:
print_flag = 'UP'
# 方案1:
# print_content:
# [lvl:badge]Nickname:Says
# ios.print_set(f'[{user_fans_lvl}:{user_title}]{nickname}:{danmaku_content}',
# tag=print_flag)
# 方案2

# 方案
# [lvl|nickname]says
ios.print_set(f'[{user_fans_lvl}|{nickname}]{danmaku_content}',
tag=print_flag)

ios.print_simple(f'[{user_fans_lvl}|{nickname}]{danmaku_content}', base=print_flag)
224 changes: 50 additions & 174 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,198 +1,74 @@
import os
import time
import multiprocessing

import interface
import iosetting as ios
import liveget as lg
import initial


__VERSION__ = 'v1.4-demo-PUSH7'


def caseA():

ios.print_set('执行初始化将重置所有文件,你确定要继续吗(Y/y)?', tag='WARNING', end='')
key = input("").strip().upper()
if key == 'Y':
file_clearer('./files')
if os.path.exists('ban_word.txt'):
os.remove('ban_word.txt')
initial.initial()
else:
return


def file_clearer(path):
if os.path.exists(path):
archive = os.listdir(path)
for file in archive:
os.remove(path + f'/{file}')


def caseB():

print('正在读取房间号...')
rid = 34162
with open('./files/settings.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
line = line.strip().split('=')
if line[0] == 'rid':
rid = int(line[1])

print('正在初始化弹幕获取器...')
x = lg.LiveInfoGet(rid=rid)

print("正在启动阅读器...")
if os.path.exists('reader.py'):
os.startfile('reader.py')
else:
os.startfile('reader.exe')

print("正在启动弹幕获取器...")
x.living_on()


def caseC():
while True:
os.system('cls')
print('|*===================================================================*|\n'
'|*| DANMAKU READER |*|\n'
f'|*| main -> 查看 {__VERSION__} |*|\n'
'|*===================================================================*|\n'
'|*| |*|\n'
'|*| B(b).禁读词列表 S(s).设置文件 |*|\n'
'|*| |*|\n'
'|*| |*|\n'
'|*| P(p).返回上一级 E(e):退出 |*|\n'
'|*| |*|\n'
'|*===================================================================*|'
)
print('>>>', end='')
get = input()
get = get.strip()
label = get[0].upper()
match label:
case 'B': os.system('ban_word.txt')
case 'S':
cwd = os.getcwd()
os.chdir(cwd+'/files')
os.system('settings.txt')
os.chdir(cwd)
case 'P': return
case 'E': exit()
case _:
print(f'{label}没有在列表中')


def settings():
settings_dict = {
'rid': None,
'min_level': None,
}

with open('./files/settings.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines()

for line in lines:
line = line.strip().split('=')
if line[0] in settings_dict:
settings_dict[line[0]] = line[1]
dict_key = settings_dict.keys()
os.system('cls')
while True:
while True:
print('\nS(s).保存并返回上一级\t\tP(p).不保存并返回上一级\n'
'E(e).保存并退出\t\tEnter.继续修改')
label = input('>>>').strip().upper()
if len(label) > 0:
label = label[0]
match label:
case 'S':
settingsave(settings_dict)
return
case 'P':
return
case 'E':
settingsave(settings_dict)
exit()
case '':
break
case _:
pass
os.system('cls')
print("当前参数如下:")
for key in dict_key:
length = len(settings_dict[key])
val = settings_dict[key]
end = ''
if length > 10:
end = '...'
val = val[0:10]
print(f'{key}: {val}' + end)
print("选择你要修改的值(输入首字母,如果相同则输入前两个字母,大小写不限):\n")
label = input('>>>').strip()
llen = len(label)
for i in dict_key:
if label.upper() == i.upper()[0:llen]:
print(f"{i}要修改为(输入E/e放弃修改)>>>", end='')
modify = input().strip()
if modify.upper() == 'E':
break
settings_dict[i] = modify
print(f'现在{i}{modify}')
break


def settingsave(dct):
_keys = dct.keys()
lines = []
for i in _keys:
line = f'{i}={dct[i]}\n'
lines.append(line)
with open('./files/settings.txt', mode='w', encoding='utf-8') as f:
f.writelines(lines)
print('保存完成,重启后生效')
__VERSION__ = 'v2.0-demo-PUSH8'
__PROJ_NAME__ = 'Danmaku Reader'
__PREFIX = 'Mainc'


def main():

f = False
multiprocessing.freeze_support()
print('正在检测初始化...')
initial.initial()
while True:
os.system('cls')
print('|*===================================================================*|\n'
'|*| DANMAKU READER |*|\n'
f'|*| {__VERSION__} |*|\n'
'|*===================================================================*|\n'
'|*| |*|\n'
'|*| A(a).初始化 B(b).启动 C(c).查看 |*|\n'
'|*| |*|\n'
'|*| |*|\n'
'|*| S(s).设置 E(e):退出 |*|\n'
'|*| |*|\n'
'|*===================================================================*|')
ios.print_set('Tips:如果你想直接修改文件,只需在c.查看中打开对应文件并直接修改,本程序中的所有改动会在重启后生效',
tag='CTRL')
ios.print_set('Tips:作者是搞机器学习的不会写QT不会搞协程,如果想帮助作者或者up,'
'欢迎来:https://github.com/MeowDWing/DanmakuReader'
',或者b站私信:吾名喵喵之翼',
tag='CTRL')
ios.print_set('Tips:设置的内容还没做好,建议所有更改现在查看里看',
tag='CTRL')
interface.interface(
proj_name=__PROJ_NAME__,
set_dict={
'b': '开始',
'c': '查看',
'l': '登录',
'r': '重置',
'u': '更新内容',
},
version=__VERSION__,
location='main',
eflag=True
)
# print('|*===================================================================*|\n'
# '|*| DANMAKU READER |*|\n'
# f'|*| {__VERSION__} |*|\n'
# '|*===================================================================*|\n'
# '|*| |*|\n'
# '|*| A(a).初始化 B(b).启动 C(c).查看 |*|\n'
# '|*| |*|\n'
# '|*| |*|\n'
# '|*| S(s).设置 E(e):退出 |*|\n'
# '|*| |*|\n'
# '|*===================================================================*|')
ios.print_details('Tips:如果你想直接修改文件,只需在c.查看中打开对应文件并直接修改,本程序中的所有改动会在重启后生效',
tag='CTRL')
ios.print_details('Tips:作者是搞机器学习的不会写QT不会搞协程,如果想帮助作者或者up,'
'欢迎来:https://github.com/MeowDWing/DanmakuReader'
',或者b站私信:吾名喵喵之翼',
tag='CTRL')
ios.print_details('Tips:设置的内容还没做好,建议所有更改现在查看里看',
tag='CTRL')

print('>>>', end='')
get = input()
get = get.strip()

label = get[0].upper()
match label:
case 'A': caseA()
case 'B': caseB()
case 'C': caseC()
case 'S': settings()
case 'E': exit()
case 'B': interface.MFunc.begin()
case 'C': interface.MFunc.check()
case 'L': interface.MFunc.login()
case 'R': interface.MFunc.reset()
case 'U': interface.MFunc.updatec()
case 'S': interface.MFunc.setting()
case 'E': exit(0)
case _:
print(f'{label}没有在列表中')


time.sleep(2)


if __name__ == '__main__':
88 changes: 42 additions & 46 deletions reader.py
Original file line number Diff line number Diff line change
@@ -4,14 +4,18 @@
import iosetting as ios
import pyttsx3
import re
import multiprocessing


class Reader:

def __init__(self):
def __init__(self, global_queue: multiprocessing.Queue):
self.danmaku_queue = deque()
self.danmaku_len = 0
self.tmp = 0
self._queue = global_queue

self.__PREFIX = 'Reader'

self.force_chasing_10 = 0 # 20
self.force_chasing_20 = 0 # 15
@@ -23,9 +27,9 @@ def __init__(self):
self.re_ban_str = ''
self.ban_word_set_initial()
self.symbol_set = set("~!@#$%^&*()_+=-`[]\\|}{;':\",./<>?~!@#¥%……&*()——+=-|}{【】、|‘’;:“”《》?,。、")
ios.print_set('本项目基于bilibili_api, 如有任何需要,请联系作者,与狐宝同在\n'
ios.print_details('本项目基于bilibili_api, 如有任何需要,请联系作者,与狐宝同在\n'
'\t\t\t------from a certain member of 保狐派', tag='CTRL')
ios.print_set('本界面为debug界面,如果程序出现任何异常,请将本界面的错误信息发与作者', tag='TIPS')
ios.print_details('本界面为debug界面,如果程序出现任何异常,请将本界面的错误信息发与作者', tag='TIPS')

self.player = TxtProcess()

@@ -46,95 +50,93 @@ def ban_word_set_initial(self):
self.ban_word_set.add(line)

self.re_ban_str = self.re_ban_str[:-1]
ios.print_set(f'屏蔽词列表{str(self.ban_word_set)}', tag='UP')
ios.print_set(f"屏蔽匹配词列表{self.re_ban_str.split('|')}", tag='UP')
if len(self.re_ban_str) == 0:
self.re_ban_str = '$没有屏蔽词'
ios.print_details(f'屏蔽词列表{str(self.ban_word_set)}', tag='UP')
ios.print_details(f"屏蔽匹配词列表{self.re_ban_str.split('|')}", tag='UP')

def reader(self):
former = ''
print('[read]wait initial')
time.sleep(5)
with open('./files/danmaku.txt', mode='w', encoding='utf-8'):
pass
while True:

if self.tmp > 50:
self.tmp = 0

# 队列加入与预处理机制
if self.danmaku_len < 50:
with open('./files/danmaku.txt', mode='r', encoding='utf-8') as f:
lines = f.readlines()
with open('./files/danmaku.txt', mode='w', encoding='utf-8'):
pass
if len(lines) != 0:

for line in lines:
line = line.strip()
re_flag = re.search(self.re_ban_str, line)
if re_flag is None:
if line not in self.ban_word_set:
line_set = set(line)
if len(line_set - self.symbol_set) > 0:
self.danmaku_queue.append(line)
self.danmaku_len += 1
ios.print_set(line+':加入了待读队列', tag='SPECIAL', head='QUEUE', special_color='#50FF50')
else:
ios.print_set(f'检测到{line}中仅包含符号,拒绝加入队列')
else:
ios.print_set(f'屏蔽{line}由于其中含有{re_flag.group()}')
while not self._queue.empty():
c: str = self._queue.get()
c = c.strip()
re_flag = re.search(self.re_ban_str, c)

if re_flag is None:
if c not in self.ban_word_set:
c_set = set(c)
if len(c_set - self.symbol_set) > 0:
self.danmaku_queue.append(c)
self.danmaku_len += 1
ios.print_details(c + ':加入了待读队列', tag='SPECIAL', head='QUEUE',
special_color='#50FF50')
else:
ios.print_details(f'检测到{c}中仅包含符号,拒绝加入队列')
else:
ios.print_details(f'屏蔽{c}由于其中含有{re_flag.group()}')

# 追赶机制
if self.danmaku_len > self.force_reset_limit:
self.danmaku_queue.clear()
self.danmaku_len = 0
ios.print_set('达到最大队列额度,弹幕姬强制重置', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_details('达到最大队列额度,弹幕姬强制重置', tag='CTRL', head='SYSTEM', prefix='CHASING')
elif self.danmaku_len > 40:
self.tmp = 0
self.popleft_n(4)
self.force_chasing_40 += 1
if self.force_chasing_40 > 5:
self.force_chasing_40 = 0
self.popleft_n(10)
ios.print_set('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_set('队列已大于40,自动跳过80%弹幕', tag='SYSTEM', prefix='CHASING')
ios.print_details('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_details('队列已大于40,自动跳过80%弹幕', tag='SYSTEM', prefix='CHASING')
elif self.danmaku_len > 30:
self.tmp = 0
self.popleft_n(3)
self.force_chasing_30 += 1
if self.force_chasing_30 > 10:
self.force_chasing_30 = 0
self.popleft_n(10)
ios.print_set('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_set('队列已大于30,自动跳过75%弹幕', tag='SYSTEM', prefix='CHASING')
ios.print_details('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_details('队列已大于30,自动跳过75%弹幕', tag='SYSTEM', prefix='CHASING')
elif self.danmaku_len > 20:
self.tmp = 0
self.popleft_n(2)
self.force_chasing_20 += 1
if self.force_chasing_20 > 15:
self.force_chasing_20 = 0
self.popleft_n(10)
ios.print_set('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_set('队列已大于20,自动跳过66%弹幕', tag='SYSTEM', prefix='CHASING')
ios.print_details('强制更新机制已减少10个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_details('队列已大于20,自动跳过66%弹幕', tag='SYSTEM', prefix='CHASING')
elif self.danmaku_len > 10:
self.tmp = 0
self.popleft_n(1)
self.force_chasing_10 += 1
if self.force_chasing_10 > 20:
self.force_chasing_10 = 0
self.popleft_n(5)
ios.print_set('强制更新机制已减少5个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_set('队列已大于10,自动跳过50%弹幕', tag='SYSTEM', prefix='CHASING')
ios.print_details('强制更新机制已减少5个弹幕', tag='CTRL', head='SYSTEM', prefix='CHASING')
ios.print_details('队列已大于10,自动跳过50%弹幕', tag='SYSTEM', prefix='CHASING')
elif self.danmaku_len > 5 and self.tmp % 3 == 0:
self.tmp = 0
self.popleft_n(1)
ios.print_set('队列已大于5,自动跳过33%弹幕', tag='SYSTEM', prefix='CHASING')
ios.print_details('队列已大于5,自动跳过33%弹幕', tag='SYSTEM', prefix='CHASING')

# 处理与读取机制
if self.danmaku_len != 0:
now = self.danmaku_queue.popleft()
if now == former:
self.danmaku_len -= 1
else:
ios.print_set(now+' 准备读取', tag='SYSTEM')
ios.print_details(now + ' 准备读取', tag='SYSTEM')
self.player.txt2audio(now)
self.danmaku_len -= 1
former = now
@@ -144,7 +146,7 @@ def reader(self):

if int(time.time()) % 100 == 0:
self.danmaku_len = len(self.danmaku_queue)
ios.print_set('当前队列数量已同步', tag='SYSTEM')
ios.print_details('当前队列数量已同步', tag='SYSTEM')

def popleft_n(self, n):
for _ in range(n):
@@ -167,11 +169,5 @@ def txt2audio(self, message: str):
self.say_engin.stop()


def main():
os.system('cls')
read = Reader()
read.reader()


if __name__ == '__main__':
main()
pass
14 changes: 14 additions & 0 deletions v2.0-demo-PUSH8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
我们更新了什么?

* 本版本开始使用多进程
* 现在,程序在一个cmd窗口内运行(即使这样会眼花缭乱)

* 现在,如果弹幕姬工作异常,可能需要登陆后使用
* 目前支持验证码登录与密码登录(登录后才能正常使用)

* 其他的优化
* 现在,在cmd窗口中界面正确的显示
* 增加了版本更新日志
* 一些不重要的bug修复


0 comments on commit 4509a08

Please sign in to comment.