    async def __print_danmaku(self):
        def write_file(filename):
            try:
                if filename and msg_i > 0:
                    tree.write(filename, encoding="UTF-8", xml_declaration=True, pretty_print=True)
            except:
                logger.warning(f"{DanmakuClient.__name__}:{self.__url}: 弹幕写入异常", exc_info=True)

        while True:
            root = etree.Element("i")
            etree.indent(root, "\t")
            tree = etree.ElementTree(root, parser=etree.XMLParser(recover=True))
            start_time = time.time()
            fmt_file_name = time.strftime(self.__file_name.encode("unicode-escape").decode()).encode().decode(
                "unicode-escape") + '.xml'
            msg_i = 0
            try:
                while True:
                    try:
                        # 无弹幕时更快分段结束
                        m = await asyncio.wait_for(self.__dm_queue.get(), timeout=1)
                    except asyncio.TimeoutError:
                        continue

                    logger.debug(f"{DanmakuClient.__name__}:{self.__url}: 弹幕queue-{m.get('msg_type')}")
                    #print(m)
                    if m.get('msg_type') == "save":
                        if 'file_name' in m and fmt_file_name != m['file_name']:
                            try:
                                if os.path.exists(m['file_name']):
                                    os.remove(m['file_name'])
                                if os.path.exists(fmt_file_name):
                                    os.rename(fmt_file_name, m.get('file_name'))
                                    logger.info(
                                        f"{DanmakuClient.__name__}:{self.__url}: 更名 {fmt_file_name} 为 {m['file_name']}")
                            except:
                                logger.exception(
                                    f"{DanmakuClient.__name__}:{self.__url}: 更名 {fmt_file_name} 为 {m['file_name']}失败")
                            fmt_file_name = m['file_name']

                        if callable(m.get('callback')):
                            m['callback']()
                        break
                    elif m.get('msg_type') == "stop":
                        try:
                            os.remove(fmt_file_name)
                        except:
                            pass
                        fmt_file_name = None
                        self.__record_task.cancel()
                        return
                    # 弹幕信息记录
                    elif m.get('msg_type') == 'danmaku':
                        try:
                            if m.get('color'):
                                color = m["color"]
                            else:
                                color = '16777215'
                            msg_time = format(time.time() - start_time, '.3f')
                            # 记录弹幕额外信息
                            timestamp = str(int(time.time()))
                            uid = str(m.get("uid",0))
                            d = etree.SubElement(root, 'd')
                            '''
                            弹幕部分的参数兼容bilibili主站弹幕 XML 文件，按顺序含义分别为：
                            1.弹幕出现时间 (秒)
                            2.弹幕类型
                            3.字号
                            4.颜色
                            5.发送时间戳
                            6.固定为 0 (主站弹幕 XML 为弹幕池 ID)
                            7.发送者 UID (主站弹幕 XML 为发送用户 ID 的 CRC32)
                            8.固定为 0 (主站弹幕 XML 为弹幕的数据库 ID)
                            '''
                            if self.__content.get("detail", None):
                                d.set('p', f"{msg_time},1,25,{color},{timestamp},0,{uid},0")
                                d.set('timestamp', timestamp)
                                d.set('uid',uid)
                                #兼容DanmakuFactory可识别用户名
                                d.set('user', m.get("name",""))
                            else:
                                d.set('p', f"{msg_time},1,25,{color},0,0,0,0")
                            d.text = m["content"]
                        except:
                            logger.warning(f"{DanmakuClient.__name__}:{self.__url}:弹幕处理异常", exc_info=True)
                            # 异常后略过本次弹幕
                            continue
                    # 礼物信息记录，支持上舰、SC、礼物，目前仅在B站开启
                    elif m.get('msg_type') in ['gift', 'super_chat' , 'guard_buy'] and self.__u == 'live.bilibili.com':
                        if not self.__content.get("detail", None):
                            continue
                        try:
                            s = etree.SubElement(root, 's')
                            s.set('timestamp', str(int(time.time())))
                            s.set('uid', str(m.get("uid")))
                            s.set('username', m.get("name",""))
                            s.set('price', str(m.get("price")))
                            s.set('type', m.get('msg_type'))
                            # 礼物名称
                            s.set('num', str(m.get('num')))
                            s.set('giftname', m.get('gift_name'))
                            s.text = m["content"]
                        except:
                            logger.warning(f"{DanmakuClient.__name__}:{self.__url}:弹幕处理异常", exc_info=True)
                            # 异常后略过本次弹幕
                            continue
                    else:
                        continue
                    msg_i += 1
                    if msg_i % 5 == 0:
                        # 每收到五条弹幕后写入 减少io
                        # 可能会写入失败 会在下次五条或者任务被取消时重新尝试写入
                        write_file(fmt_file_name)
            finally:
                # 发生异常(被取消)时写入 避免丢失未写入
                write_file(fmt_file_name)
弹幕部分的参数兼容bilibili主站弹幕 XML 文件，按顺序含义分别为：
1.弹幕出现时间 (秒)
2.弹幕类型
3.字号
4.颜色
5.发送时间戳
6.固定为 0 (主站弹幕 XML 为弹幕池 ID)
7.发送者 UID (主站弹幕 XML 为发送用户 ID 的 CRC32)
8.固定为 0 (主站弹幕 XML 为弹幕的数据库 ID)
biliup/Danmaku/__init__.py