def __init__(self, app_path: Path, base_path: Path): """Initialises the configuration class """ _logger.debug(f'__init__ app_path "{app_path}"') # Initialize self._name = app_path.stem self._base_path = base_path # Folders self._data_path = Path(self._base_path, 'data').resolve() self._output_path = Path(self._base_path, 'output').resolve() self._base_data_frame_path = Path(self._output_path, 'dataframes').resolve() self._data_frame_path = Path(self._base_data_frame_path, date.today().strftime("%y-%m-%d")).resolve() self._plot_base_path = Path(self._output_path, 'plots').resolve() self._plot_path = Path(self._plot_base_path, date.today().strftime("%y-%m-%d")).resolve() # Ensure directories pre-exist self._data_path.mkdir(parents=True, exist_ok=True) self._data_frame_path.mkdir(parents=True, exist_ok=True) self._plot_path.mkdir(parents=True, exist_ok=True) _logger.debug(f'data path: "{self._data_path}"' f'data frame path: "{self._data_path}"' f'plot path: "{self._plot_path}"') self._config_plots = ConfigParser.parse(Path(_PLOTS_CONFIG_FILENAME))
def main(): global sPath global alive global running global mConfig, mMap, mExplain global display, display1, display2 global log global nRoom global aColour global localFile global aBlock global notifyMode global beatClock # use crafted display function to ease the migration from python3 to python2 and to accommodate to different terminal coding in different system # display1 is normal displayer, while display2 is a separate displayer running in special thread, implementing the display interval # in each case, display1 shall be a instant displayer; thus, use display1 to output diagnostic message display1 = Displayer(0).display display = display1 mConfigBak = mConfig.copy() try: parser1 = ConfigParser(mConfig, mExplain, mMap, 'display danmu message in bilibili live') useCLI = True if len(sys.argv) > 1 else False if (not os.path.exists(sPath)): sDir = os.path.split(sys.argv[0])[0] sFile = os.path.join(sDir, sPath) if (os.path.exists(sFile)): sPath = sFile else: display1('配置文件 {} 不存在'.format(sPath)) sPath = None # parse configuration from file and from command line option mData = parser1.parse(sPath, useCLI) mConfig = mData except Exception as e: display1('读取配置出错:', e, sep='\n') display1('退回默认配置') mConfig = mConfigBak if (mConfig['nDelay'] > 0): # danmu message display interval is enabled, using threaded displayer display2 = Displayer(1, mConfig['nDelay']).display display = display2 aColour = [(x + 30 if x < 10 else x + 80) for x in mConfig['aColour']] if (mConfig['verbose']): log = display1 else: def log(*aArgs, **mArgs): pass if (mConfig['block']): # it seems that two format of flooding messages are existing aBlock = ['bilibili-(゜-゜)つロ乾杯~', '- ( ゜- ゜)つロ 乾杯~ - bilibili'] if (mConfig['notify']): notifyMode = 2 else: notifyMode = 1 log(mConfig) nRoom = mConfig['nRoom'] or int(input('room ID:')) running = True socket.setdefaulttimeout(10) while running: try: try: sServer, nRoom, sHoster, sTitle = getRoom(nRoom) except urllib.error.HTTPError as e: if (e.code == 404): display1('找不到该房间,请重新输入房间号') nRoom = int(input('room ID:')) continue else: raise if (mConfig['write']): sTime = time.strftime('%m%d_%H%M%S-') sName = sHoster + '-' + sTitle sName = re.sub(r'[^\w_\-.()]', '-', sName) sFileName = '{}{}.txt'.format(sTime, sName) localFile = open(sFileName, 'a', encoding='utf-8') log('弹幕服务器 ' + sServer) aAddr1 = (sServer, 788) sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock1.connect(aAddr1) except TimeoutError as e: sock1.close() display1('到弹幕服务器的连接失败,尝试更换地址') if (sServer == 'livecmt-1.bilibili.com'): sServer = 'livecmt-2.bilibili.com' else: sServer = 'livecmt-1.bilibili.com' log('弹幕服务器 ' + sServer) aAddr1 = (sServer, 788) sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock1.connect(aAddr1) log('地址为 ', *sock1.getpeername()) nUid = int(100000000000000 + 200000000000000 * random.random()) # a random meaningless user ID #bPayload = b'{"roomid":%d,"uid":%d}' % (nRoom, nUid); bPayload = ('{"roomid":%d,"uid":%d}' % (nRoom, nUid)).encode('utf-8') nLength = len(bPayload) + 16 bReq = struct.pack('>IIII', nLength, 0x100001, 0x7, 0x1) bReq += bPayload sock1.sendall(bReq) alive = True bHeartBeat = struct.pack('>IIII', 0x10, 0x100001, 0x2, 0x1) sock1.sendall(bHeartBeat) # send heartbeat message per 30 seconds interval = SetInterval(lambda: (sock1.sendall(bHeartBeat)), 30) interval.start() # capture CR in stdin to send hearbeat in order to fetch freshed online count if (not beatClock): beatClock = interval.clock t = threading.Thread(target=notify) t.daemon = 1 t.start() else: beatClock = interval.clock handler2(sock1) except (socket.timeout, TimeoutError) as e: display1('连接超时,重试...') continue except SocketDied as e: display1('连接被关闭,程序重启...') continue except BaseException as e: if (isinstance(e, KeyboardInterrupt)): display1('程序退出') running = False elif (sys.version[0] == '3' and isinstance(e, ConnectionResetError)): # ConnectionResetError is not supported in python2 display1(e) display1('到服务器的连接被断开,尝试重新连接...') continue else: with open('danmu_error.log', 'ab') as f1: # record error log f1.write(('\n' + (str(e))).encode('utf-8')) raise finally: alive = False if ('interval' in locals()): interval.stop() if ('sock1' in locals()): sock1.close() if (localFile): display1('弹幕已保存到文件 {}'.format(localFile.name)) localFile.close()