Esempio n. 1
0
class Display():
    def __init__(self):
        self.console = Console(force_terminal=True, color_system='truecolor')
        self.console._environ['TERM'] = 'SMART'
        self._lock = threading.Lock()
        self.live_infos = Infos()
        self.last_time = datetime.datetime.now()
        self.last_net_sent = 0.0
        self.last_net_recv = 0.0

    def generate_info(self, row_id: int, live_info: dict) -> Info:
        info = None
        while info is None:
            try:
                if live_info is not None and 'live_status' in live_info:
                    info = Info(
                        row_id=row_id,
                        room_id=live_info['room_id'],
                        anchor=live_info['uname'],
                        title=live_info['title'],
                        live_status=live_info['live_status'],
                        record_status=live_info['recording'],
                        start_time=datetime.datetime.fromtimestamp(
                            live_info['live_start_time']).strftime(
                                '%Y-%m-%d %H:%M:%S'),
                        record_start_time=live_info['record_start_time'],
                        queue_status=live_info['queue_status'],
                        finish_time=live_info['finish_time'])
                else:
                    break
            except Exception as e:
                continue
        return info

    def create_info_table(self, live_infos):
        dct = {0: 0, 1: 100, 2: 50, 4: 10}
        dct2 = {0: 0, 1: 100, 2: 50, 3: 30}
        infos = sorted([
            self.generate_info(rid, live_infos[key])
            for key, rid in zip(live_infos.keys(), range(len(live_infos)))
        ],
                       key=lambda i: dct[i.live_status] * 100 + 100 * dct2[
                           i.record_status] - i.row_id + i.queue_status,
                       reverse=True)
        table1 = Table("行号",
                       "房间ID",
                       "主播",
                       "直播标题",
                       "直播状态",
                       "录制状态",
                       "开播时间",
                       "录制时长",
                       "队列情况",
                       "完成时间",
                       title="%s" %
                       datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                       box=box.SIMPLE)

        for info in infos:
            table1.add_row(str(info.row_id), info.room_id_map, info.anchor_map,
                           info.title_map, info.live_status_map,
                           info.record_status_map, info.start_time_map,
                           info.record_time, info.queue_status_map,
                           info.finish_time_map)

        table2 = Table("CPU", "Memory", "NetSent", "NetRecv", box=box.SIMPLE)

        time_now = datetime.datetime.now()
        now_recv = psutil.net_io_counters().bytes_recv
        now_sent = psutil.net_io_counters().bytes_sent

        table2.add_row(
            str(psutil.cpu_percent(None)) + '%' + '  %.2fGHz' %
            (psutil.cpu_freq().current / 1000.0),
            str(psutil.virtual_memory().percent) + '%' + '  %s/%s' %
            (bytes2human(psutil.virtual_memory().used),
             bytes2human(psutil.virtual_memory().total)),
            bytes2human((now_sent - self.last_net_sent) /
                        (time_now - self.last_time).total_seconds()) + '/s',
            bytes2human((now_recv - self.last_net_recv) /
                        (time_now - self.last_time).total_seconds()) + '/s')

        self.last_time = time_now
        self.last_net_sent = now_sent
        self.last_net_recv = now_recv

        return RenderGroup(table1, table2)

    def run(self):
        # self.console.clear()
        with Live(console=self.console, auto_refresh=False) as live:
            while True:
                try:
                    live.update(self.create_info_table(self.live_infos.copy()),
                                refresh=True)
                    time.sleep(1)
                except Exception as e:
                    logger.critical(e)
                    continue
Esempio n. 2
0
class Queue():
    def __init__(self):
        self._lock = threading.Lock()
        self._lock2 = threading.Lock()
        self.config = Config()
        self.infos = Infos()
        self.queue = []
        self.qname = ''
        self.func = lambda x:time.sleep(400)
        self.threadRecorder = threadRecorder()
        self.base_num = 0

    def func_call(self, key):
        with self._lock2:
            logger.info('%s 开始%s' % (self.infos.copy()[key]['uname'], self.qname))
            live_info = self.infos.copy()[key]
            live_info['queue_status'] = self.base_num
            self.infos.update(key, live_info)
            self.func(key)
            live_info['queue_status'] = self.base_num + 500
            live_info['finish_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            self.infos.update(key, live_info)

    def update_status(self):
        for key in self.queue:
            live_info = self.infos.copy()[key]
            live_info['queue_status'] = self.base_num + self.queue.index(key) + 1
            self.infos.update(key, live_info)

    def enqueue(self, key):
        with self._lock:
            live_info = self.infos.copy()[key]
            live_info['queue_status'] = self.base_num + len(self.queue) + 1
            if key not in self.queue:
                self.queue.append(key)
                logger.info('%s 进入%s等待队列' % (self.infos.copy()[key]['uname'],self.qname))
            else:
                self.queue.remove(key)
                self.queue.append(key)
                logger.info('%s 在%s等待队列中的状态更新了' % (self.infos.copy()[key]['uname'],self.qname))
            self.infos.update(key, live_info)

    def dequeue(self):
        if self._lock2.locked():
            return None
        with self._lock:
            with self._lock2:
                if len(self.queue) > 0:
                    key = self.queue[0]
                    del self.queue[0]
                    self.update_status()
                    logger.info('%s 退出%s等待队列' % (self.infos.copy()[key]['uname'],self.qname))
                    return key
                else:
                    return None

    def run(self):
        self.threadRecorder.add('%s heartbeat' % self.qname,self.heartbeat,None,True)
        while True:
            time.sleep(1)
            if len(self.queue) > 0:
                key = self.dequeue()
                if key is not None:
                    self.threadRecorder.add('%s_%s' % (self.qname,key),self.func_call,[key,],False)

    def heartbeat(self):
        while True:
            time.sleep(180)
            logger.info('当前%s队列情况: %s' % (self.qname, ' '.join([self.infos.copy()[key]['uname'] for key in self.queue])))
Esempio n. 3
0
class Live():
    def __init__(self):
        self.config = Config()
        self.cookies = login()
        logger.info(self.cookies)
        self.base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
                                      self.config.config['output']['path'])
        self.live_infos = Infos()
        self.display = Display()
        self.decoder = Decoder()
        self.uploader = Upload()
        self.threadRecorder = threadRecorder()
        logger.info('基路径:%s' % (self.base_path))
        self.load_room_info()
        self.get_live_url()
        logger.info('初始化完成')

    def create_duration(self, start_time, end_time):
        t = datetime.datetime.now()
        tt = t.strftime('%Y%m%d %H%M%S')
        if start_time == end_time == '0':
            return '0'
        tmp = datetime.datetime.strptime(tt.split(' ')[0] + ' %s' % start_time, '%Y%m%d %H%M%S')
        if t > tmp:
            base_time1 = tt.split(' ')[0]
            base_time2 = (t + datetime.timedelta(days=1)).strftime('%Y%m%d %H%M%S').split(' ')[0]
        else:
            base_time1 = (t - datetime.timedelta(days=1)).strftime('%Y%m%d %H%M%S').split(' ')[0]
            base_time2 = tt.split(' ')[0]
        if start_time > end_time:
            start_time = '%s %s' % (base_time1, start_time)
            end_time = '%s %s' % (base_time2, end_time)
        else:
            start_time = '%s %s' % (tt.split(' ')[0], start_time)
            end_time = '%s %s' % (tt.split(' ')[0], end_time)
        return '%s-%s' % (start_time, end_time)

    def check_live(self, key):
        duration = self.live_infos.get(key)['duration']
        if duration == '0':
            return True
        lst = duration.split('-')
        now_time = datetime.datetime.now()
        if len(lst) == 2:
            start_time = datetime.datetime.strptime(lst[0], '%Y%m%d %H%M%S')
            end_time = datetime.datetime.strptime(lst[1], '%Y%m%d %H%M%S')
            if now_time > start_time and now_time < end_time:
                return True
            else:
                logger.debug('%s[RoomID:%s]不在直播时间段' % (self.live_infos.get(key)['uname'], key))
                return False
        else:
            return False

    def load_room_info(self):
        live_infos = self.live_infos.copy()
        for lst in self.config.config['live']['room_info']:
            if lst[0] not in live_infos:
                live_info = {}
                live_info['record_start_time'] = ''
                live_info['queue_status'] = 0
                live_info['recording'] = 0
                live_info['finish_time'] = ''
            else:
                live_info = live_infos[lst[0]]
            live_info['need_rec'] = lst[1]
            live_info['need_mask'] = lst[2]
            live_info['maxsecond'] = lst[3]
            live_info['need_upload'] = lst[4]
            live_info['duration'] = self.create_duration(lst[5], lst[6])
            live_info['cookies'] = self.cookies
            live_info['base_path'] = self.base_path
            self.live_infos.update(lst[0],live_info)

    def load_realtime(self):
        '''
        实时加载配置,更新房间信息
        '''
        self.config.load_cfg()
        # logger.info(self.config.config)
        room_lst = [i[0] for i in self.config.config['live']['room_info']]
        del_lst = []
        for key in self.live_infos.copy():
            if key not in room_lst:
                del_lst.append(key)
        for key in del_lst:
            self.live_infos.delete(key)
        self.load_room_info()

    def judge_in(self, key):
        room_lst = [i[0] for i in self.config.config['live']['room_info']]
        if key not in room_lst:
            return False
        return True

    def judge_download(self,key):
        if not self.judge_in(key):
            return False
        live_info = self.live_infos.copy()[key]
        if live_info['live_status'] != 1:
            live_info['recording'] =0
            self.live_infos.update(key,live_info)
            return False
        elif not self.check_live(key) and live_info['live_status'] == 1:
            live_info['recording'] =2
            self.live_infos.update(key,live_info)
            return False
        elif self.check_live(key) and live_info['live_status'] == 1 and live_info['need_rec'] == '0':
            live_info['recording']=3
            self.live_infos.update(key,live_info)
            return False
        elif live_info['live_status'] == 1 and live_info['need_rec'] == '1':
            live_info['recording']=1
            self.live_infos.update(key,live_info)
            return True
        else:
            logger.warning('%s[RoomID:%s]进入了未知的分支呢' % (live_info['uname'],key))
            live_info['recording'] =0
            self.live_infos.update(key,live_info)
            return False

    def get_live_url(self):
        '''
        获取所有监听直播间的信息
        '''
        room_lst = [i[0] for i in self.config.config['live']['room_info']]
        for id in room_lst:
            info = None
            while info is None:
                try:
                    info = live.get_room_info(id, cookies=self.cookies)
                    time.sleep(0.3)
                except:
                    logger.error('[RoomID:%s]获取信息失败,重新尝试' % (id))
                    continue
            live_info = self.live_infos.copy()[id]
            live_info['room_id'] = id
            live_info['real_id'] = info['room_info']['room_id']
            try:
                if live_info['live_status'] != 1 and info['room_info']['live_status'] == 1:
                    logger.info('%s[RoomID:%s]开播了' % (live_info['uname'], id))
                    toaster = ToastNotifier()
                    toaster.show_toast("开播通知",
                                       '%s[RoomID:%s]开播了' % (live_info['uname'], id),
                                       icon_path=None,
                                       duration=3)
            except:
                pass
            try:
                live_info['live_status'] = info['room_info']['live_status']
                live_info['uid'] = info['room_info']['uid']
                live_info['uname'] = info['anchor_info']['base_info']['uname']
                live_info['save_name'] = '%s_%s.flv' % (
                    live_info['uname'], time.strftime("%Y%m%d%H%M%S", time.localtime()))
                live_info['title'] = info['room_info']['title']
                live_info['live_start_time'] = info['room_info']['live_start_time']
                self.live_infos.update(id,live_info)
                logger.debug(
                    '%s[RoomID:%s]直播状态\t%s' % (live_info['uname'], id, live_info['live_status']))
            except Exception as e:
                logger.critical(e)
                logger.error('[RoomID:%s]房间信息更新失败' % (id))
                logger.error(info)
        # logger.info(self.live_infos.copy())

    def get_stream(self, key):
        '''
        获取直播流
        :param key: 房间显示id
        :return: stream
        '''
        if not self.judge_in(key):
            return None
        live_info = self.live_infos.copy()[key]
        logger.info('%s[RoomID:%s]获取直播流' % (live_info['uname'], key))
        session = streamlink.Streamlink()
        session.set_option("http-cookies", self.cookies)
        session.set_option("http-headers", headers)
        log_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'log', 'stream.log')
        session.set_loglevel("debug")
        session.set_logoutput(open(log_path, 'a',encoding='utf-8'))
        streams = None
        while streams is None:
            try:
                streams = session.streams('https://live.bilibili.com/%s' % key)
            except:
                logger.warning('%s[RoomID:%s]获取直播流失败,正在重试' % (live_info['uname'], key))
                time.sleep(1)
        if streams == {}:
            logger.error('%s[RoomID:%s]未获取到直播流,可能是下播或者网络问题' % (live_info['uname'], key))
            return None
        if 'best' in streams:
            logger.info('%s[RoomID:%s]获取到best直播流' % (live_info['uname'], key))
            return streams['best']
        elif 'source' in streams:
            logger.info('%s[RoomID:%s]获取到source直播流' % (live_info['uname'], key))
            return streams['source']
        elif 'worst' in streams:
            logger.info('%s[RoomID:%s]获取到worst直播流' % (live_info['uname'], key))
            return streams['worst']
        else:
            logger.info('%s[RoomID:%s]未获取到直播流' % (live_info['uname'], key))
            return None

    def unlive(self, key, unlived):
        if not self.judge_in(key):
            return None
        live_info = self.live_infos.copy()[key]
        logger.info('%s[RoomID:%s]似乎下播了' % (live_info['uname'], key))
        live_info['recording'] = 0
        logger.info('%s[RoomID:%s]录制结束,录制了%.2f分钟' % (live_info['uname'], key, (
                datetime.datetime.now() - datetime.datetime.strptime(
            live_info['record_start_time'],
            '%Y-%m-%d %H:%M:%S')).total_seconds() / 60.0))
        live_info['record_start_time'] = ''
        if unlived:
            logger.info('%s[RoomID:%s]确认下播,加入转码上传队列' % (live_info['uname'], key))
            # if live_info['need_upload'] == '1':
            #     live_info['filename'] = live_info['uname'] + live_info['duration'].split('-')[0].split(' ')[0]
            #     live_info['filepath'] = os.path.join(self.base_path, live_info['uname'], '%s_%s' % (live_info['uname'], live_info['duration'].split('-')[0].split(' ')[0]))
            #     if live_info['need_mask'] == '1':
            #         live_info['filepath'] += '_mask.mp4'
            #     else:
            #         live_info['filepath'] += '.mp4'
            self.decoder.enqueue(key)
        self.live_infos.update(key,live_info)    

    def download_live(self, key):
        if not self.judge_in(key):
            return None
        save_path = os.path.join(self.base_path, self.live_infos.get(key)['uname'], 'recording')
        logger.info('%s[RoomID:%s]准备下载直播流,保存在%s' % (self.live_infos.get(key)['uname'], key, save_path))
        self.live_infos.get(key)['recording'] = 1
        if not os.path.exists(save_path):
            os.makedirs(save_path)

        stream = self.get_stream(key)
        if stream is None:
            logger.error('%s[RoomID:%s]获取直播流失败' % (self.live_infos.get(key)['uname'], key))
            self.live_infos.get(key)['record_start_time'] = ''
            self.live_infos.get(key)['recording'] = 0
            return
        filename = os.path.join(save_path, self.live_infos.get(key)['save_name'])
        self.live_infos.get(key)['record_start_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        try:
            fd = stream.open()
        except Exception as e:
            self.unlive(key,unlived=False)
            logger.critical('%s[RoomID:%s]fd open error' % (self.live_infos.get(key)['uname'], key))
            logger.error(e)
            return
        with open(filename, 'wb') as f:
            while self.judge_in(key) and self.live_infos.get(key)['live_status'] == 1 and self.live_infos.get(key)[
                'need_rec'] == '1' and self.check_live(key):
                try:
                    data = fd.read(1024 * 8)
                    if len(data) > 0:
                        f.write(data)
                    else:
                        fd.close()
                        logger.warning('%s[RoomID:%s]直播流断开,尝试重连' % (self.live_infos.get(key)['uname'], key))
                        stream = self.get_stream(key)
                        if stream is None:
                            logger.warning('%s[RoomID:%s]重连失败' % (self.live_infos.get(key)['uname'], key))
                            self.unlive(key, True)
                            return
                        else:
                            logger.info('%s[RoomID:%s]重连成功' % (self.live_infos.get(key)['uname'], key))
                            fd = stream.open()
                except Exception as e:
                    fd.close()
                    self.unlive(key,unlived=False)
                    logger.critical('%s[RoomID:%s]遇到了什么问题' % (self.live_infos.get(key)['uname'], key))
                    logger.error(e)
                    return
        fd.close()
        self.unlive(key, True)

    def run(self):
        while True:
            time.sleep(1)
            self.load_realtime()
            self.get_live_url()
            live_infos = self.live_infos.copy()
            for key in live_infos:
                if live_infos[key]['recording'] != 1 and self.judge_download(key):
                    self.threadRecorder.add('download_live_%s' % (key),self.download_live,[key,],False)
                time.sleep(0.2)

    def start(self):
        self.threadRecorder.add('display_run',self.display.run,None,False)
        self.threadRecorder.add('decoder_run',self.decoder.run,None,False)
        self.threadRecorder.add('uploader_run',self.uploader.run,None,False)
        self.threadRecorder.add('live_run',self.run,None,False)
Esempio n. 4
0
class History():
    def __init__(self):
        self._lock = threading.Lock()
        self.live_infos = Infos()
        self.base_path = os.path.join(
            os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
            'history')
        self.init()
        logger.debug('History模块初始化完成')

    def init(self):
        if not os.path.exists(self.base_path):
            os.mkdir(self.base_path)
        for key in self.live_infos.copy():
            history_file = os.path.join(
                self.base_path,
                '%s_%s' % (key, self.live_infos.get(key)['uname']))
            if not os.path.exists(history_file):
                with open(history_file, 'w', encoding='utf-8') as a:
                    pass

    def add_info(self, key, para, output):
        self.init()
        history_file = os.path.join(
            self.base_path, '%s_%s' % (key, self.live_infos.get(key)['uname']))
        with self._lock:
            with open(history_file, 'a', encoding='utf-8') as a:
                a.write(
                    '####📢 %s, 当前%s状态为: %s, 当前时间: %s\n' %
                    (output, para, self.live_infos.get(key)[para],
                     datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    def heartbeat(self):
        while True:
            try:
                time.sleep(600)
                self.init()
                for key in self.live_infos.copy():
                    history_file = os.path.join(
                        self.base_path,
                        '%s_%s' % (key, self.live_infos.get(key)['uname']))
                    with self._lock:
                        with open(history_file, 'a', encoding='utf-8') as a:
                            a.write('####✨ 当前时间: %s\n' %
                                    (datetime.datetime.now().strftime(
                                        '%Y-%m-%d %H:%M:%S')))
                            a.write('录制时段: %s\n' %
                                    (self.live_infos.get(key)['duration']))
                            a.write('直播状态: %s\n' %
                                    (self.live_infos.get(key)['live_status']))
                            a.write('录制状态: %s\n' %
                                    (self.live_infos.get(key)['recording']))
                            a.write('是否录制: %s\n' %
                                    (self.live_infos.get(key)['need_rec']))
                            a.write('是否遮挡: %s\n' %
                                    (self.live_infos.get(key)['need_mask']))
                            a.write('是否上传: %s\n' %
                                    (self.live_infos.get(key)['need_upload']))
            except Exception as e:
                logger.critical(e)
                continue