濮阳杆衣贸易有限公司

主頁 > 知識庫 > python實現(xiàn)的B站直播錄制工具

python實現(xiàn)的B站直播錄制工具

熱門標簽:溫州旅游地圖標注 幫人做地圖標注收費算詐騙嗎 外呼不封號系統(tǒng) 遼寧400電話辦理多少錢 電信營業(yè)廳400電話申請 悟空智電銷機器人6 江蘇房產電銷機器人廠家 蘇州電銷機器人十大排行榜 荊州云電銷機器人供應商

項目地址:

https://github.com/Redlnn/blive_record

前言

  • 作者: Red_lnn
  • 不允許將本項目運用于非法以及違反B站用戶協(xié)議的用途
  • 僅支持單個主播,多個主播請復制多份并分開單獨啟動
  • 運行時如要停止錄制并退出,請按鍵盤 Ctrl+C
  • 如要修改錄制設置,請以純文本方式打開.py文件
  • 利用 FFmpeg 直接抓取主播推送的流,無需打開瀏覽器
  • 有新功能需求請直接提 Pull requests
  • 建議錄制為 flv 格式(默認),以防止意外中斷導致錄制文件損壞,若要進行剪輯可使用 FFmpeg 轉換為 mp4 文件后再倒入到剪輯軟件(使用 FFmpeg 轉換 flv 為 mp4 : ffmpeg -i {input}.flv -c:v copy -c:a copy {output}.mp4)

使用方式

1.安裝 Python(>=3.7) 并設置環(huán)境變量

2.打開終端或命令行進入本腳本所在目錄

3.通過 pip 安裝必須的第三方庫

Windows:

pip install -r requirements.txt

Linux:

python3 -m pip install -r requirements.txt

4.下載 ffmpeg 并正確設置環(huán)境變量(下載地址)
5.Windows 直接雙擊運行start.bat
6.Linux 先運行 chmod +x start.sh 再運行 ./start.sh

主要代碼

blive_record.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
*--------------------------------------*
 B站直播錄播姬 By: Red_lnn
 僅支持單個主播,多個主播請復制多份并分開單獨啟動
 運行時如要停止錄制并退出,請按鍵盤 Ctrl+C
 如要修改錄制設置,請以純文本方式打開.py文件
 利用ffmpeg直接抓取主播推送的流,不需要打開瀏覽器
*--------------------------------------*
"""

# import ffmpy3  # noqa
import logging
import os
import signal
import sys
import threading
import time
import traceback
from json import loads
from logging import handlers
from subprocess import PIPE, Popen, STDOUT

import requests
from regex import match

# 導入配置
from config import *   # noqa

record_status = False  # 錄制狀態(tài),True為錄制中
kill_times = 0  # 嘗試強制結束FFmpeg的次數(shù)

logging.addLevelName(15, 'FFmpeg')  # 自定義FFmpeg的日志級別
logger = logging.getLogger('Record')
logger.setLevel(logging.DEBUG)

fms = '[%(asctime)s %(levelname)s] %(message)s'
# datefmt = "%Y-%m-%d %H:%M:%S"
datefmt = "%H:%M:%S"

default_handler = logging.StreamHandler(sys.stdout)
if debug:
    default_handler.setLevel(logging.DEBUG)
elif verbose:
    default_handler.setLevel(15)
else:
    default_handler.setLevel(logging.INFO)
default_handler.setFormatter(logging.Formatter(fms, datefmt=datefmt))
logger.addHandler(default_handler)

if save_log:
    # file_handler = logging.FileHandler("debug.log", mode='w+', encoding='utf-8')
    if not os.path.exists(os.path.join('logs')):
        os.mkdir(os.path.join('logs'))
    file_handler = handlers.TimedRotatingFileHandler(os.path.join('logs', 'debug.log'), 'midnight', encoding='utf-8')
    if debug:
        default_handler.setLevel(logging.DEBUG)
    else:
        default_handler.setLevel(15)
    file_handler.setFormatter(logging.Formatter(fms, datefmt=datefmt))
    logger.addHandler(file_handler)


def get_timestamp() -> int:
    """
    獲取當前時間戳
    """
    return int(time.time())


def get_time() -> str:
    """
    獲取格式化后的時間
    """
    time_now = get_timestamp()
    time_local = time.localtime(time_now)
    dt = time.strftime("%Y%m%d_%H%M%S", time_local)
    return dt


def record():
    """
    錄制過程中要執(zhí)行的檢測與判斷
    """
    global p, record_status, last_record_time, kill_times  # noqa
    while True:
        line = p.stdout.readline().decode()
        p.stdout.flush()
        logger.log(15, line.rstrip())
        if match('video:[0-9kmgB]* audio:[0-9kmgB]* subtitle:[0-9kmgB]*', line) or 'Exiting normally' in line:
            record_status = False  # 如果FFmpeg正常結束錄制則退出本循環(huán)
            break
        elif match('frame=[0-9]', line) or 'Opening' in line:
            last_record_time = get_timestamp()  # 獲取最后錄制的時間
        elif 'Failed to read handshake response' in line:
            time.sleep(5)  # FFmpeg讀取m3u8流失敗,等個5s康康會不會恢復
            continue
        time_diff = get_timestamp() - last_record_time  # 計算上次錄制到目前的時間差
        if time_diff >= 65:
            logger.error('最后一次錄制到目前已超65s,將嘗試發(fā)送終止信號')
            logger.debug(f'間隔時間:{time_diff}s')
            kill_times += 1
            p.send_signal(signal.SIGTERM)  # 若最后一次錄制到目前已超過65s,則認為FFmpeg卡死,嘗試發(fā)送終止信號
            time.sleep(0.5)
            if kill_times >= 3:
                logger.critical('由于無法結束FFmpeg進程,將嘗試自我了結')
                sys.exit(1)
        if 'Immediate exit requested' in line:
            logger.info('FFmpeg已被強制結束')
            break
        if p.poll() is not None:  # 如果FFmpeg已退出但沒有被上一個判斷和本循環(huán)第一個判斷捕捉到,則當作異常退出
            logger.error('ffmpeg未正常退出,請檢查日志文件!')
            record_status = False
            break


def main():
    global p, room_id, record_status, last_record_time, kill_times  # noqa
    while True:
        record_status = False
        while True:
            logger.info('------------------------------')
            logger.info(f'正在檢測直播間:{room_id}')
            try:
                room_info = requests.get(f'https://api.live.bilibili.com/room/v1/Room/get_info?room_id={room_id}',
                                         timeout=5)
            except (requests.exceptions.ReadTimeout, requests.exceptions.Timeout, requests.exceptions.ConnectTimeout):
                logger.error(f'無法連接至B站API,等待{check_time}s后重新開始檢測')
                time.sleep(check_time)
                continue
            live_status = loads(room_info.text)['data']['live_status']
            if live_status == 1:
                break
            elif live_status == 0:
                logger.info(f'沒有開播,等待{check_time}s重新開始檢測')
            time.sleep(check_time)
        if not os.path.exists(os.path.join('download')):
            try:
                os.mkdir(os.path.join('download'))
            except:  # noqa
                logger.error(f'無法創(chuàng)建下載文件夾 ↓\n{traceback.format_exc()}')
                sys.exit(1)
        if os.path.isfile(os.path.join('download')):
            logger.error('存在與下載文件夾同名的文件')
            sys.exit(1)
        logger.info('正在直播,準備開始錄制')
        m3u8_list = requests.get(
            f'https://api.live.bilibili.com/xlive/web-room/v1/playUrl/playUrl?cid={room_id}platform=h5qn=10000')
        m3u8_address = loads(m3u8_list.text)['data']['durl'][0]['url']
        # 下面命令中的timeout單位為微秒,10000000us為10s(https://www.cnblogs.com/zhifa/p/12345376.html)
        command = ['ffmpeg', '-rw_timeout', '10000000', '-timeout', '10000000', '-listen_timeout', '10000000',
                   '-headers',
                   '"Accept: */*? Accept-Encoding: gzip, deflate, br? Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;'
                   f'q=0.7,zh-CN;q=0.6,ru;q=0.5? Origin: https://live.bilibili.com/{room_id}? '
                   'User-Agent: Mozilla/5.0 (Windows NT 10.0;Win64; x64) '
                   'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36?"', '-i',
                   m3u8_address, '-c:v', 'copy', '-c:a', 'copy', '-bsf:a', 'aac_adtstoasc',
                   '-f', 'segment', '-segment_time', str(segment_time), '-segment_start_number', '1',
                   os.path.join('download', f'[{room_id}]_{get_time()}_part%03d.{file_extensions}'), '-y']
        if debug:
            logger.debug('FFmpeg命令如下 ↓')
            command_str = ''
            for _ in command:
                command_str += _
            logger.debug(command_str)
        p = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False)
        record_status = True
        start_time = last_record_time = get_timestamp()
        try:
            t = threading.Thread(target=record)
            t.start()
            while True:
                if not record_status:
                    break
                if verbose or debug:
                    time.sleep(20)
                    logger.info(f'--==>>> 已錄制 {round((get_timestamp() - start_time) / 60, 2)} 分鐘 ==--')
                else:
                    time.sleep(60)
                    logger.info(f'--==>>> 已錄制 {int((get_timestamp() - start_time) / 60)} 分鐘 ==--')
                if not record_status:
                    break
        except KeyboardInterrupt:
            # p.send_signal(signal.CTRL_C_EVENT)
            logger.info('停止錄制,等待ffmpeg退出后本程序會自動退出')
            logger.info('若長時間卡住,請再次按下ctrl+c (可能會損壞視頻文件)')
            logger.info('Bye!')
            sys.exit(0)
        kill_times = 0
        logger.info('FFmpeg已退出,重新開始檢測直播間')
        # time.sleep(check_time)


if __name__ == '__main__':
    logger.info('B站直播錄播姬 By: Red_lnn')
    logger.info('如要停止錄制并退出,請按鍵盤 Ctrl+C')
    logger.info('如要修改錄制設置,請以純文本方式打開.py文件')
    logger.info('準備開始錄制...')
    time.sleep(0.3)
    try:
        main()
    except KeyboardInterrupt:
        logger.info('Bye!')
        sys.exit(0)

config.py(配置文件)

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
*------------以下為可配置項-------------*
"""
# room_id = 1151716  # 萵苣某人
# room_id = 1857249  # Red_lnn
room_id = 1151716  # 要錄制的B站直播間的直播間ID
segment_time = 3600  # 錄播分段時長(單位:秒)
check_time = 60  # 開播檢測間隔(單位:秒)
file_extensions = 'flv'  # 錄制文件后綴名(文件格式)
verbose = True  # 是否打印ffmpeg輸出信息到控制臺
debug = False  # 是否顯示并保存調試信息(優(yōu)先級高于 verbose)
save_log = True  # 是否保存日志信息為文件,同一天多次啟動本腳本會共用同一個日志文件,每天凌晨分割一次日志文件
"""
*------------以上為可配置項-------------*
"""

以上就是python實現(xiàn)的B站直播錄播工具的詳細內容,更多關于python B站直播錄播的資料請關注腳本之家其它相關文章!

您可能感興趣的文章:
  • python實現(xiàn)錄制全屏和選擇區(qū)域錄屏功能
  • python實現(xiàn)錄屏功能(親測好用)
  • python 偷懶技巧——使用 keyboard 錄制鍵盤事件
  • Python實現(xiàn)播放和錄制聲音的功能
  • Python實現(xiàn)屏幕錄制功能的代碼
  • 使用Python來做一個屏幕錄制工具的操作代碼
  • Python+opencv+pyaudio實現(xiàn)帶聲音屏幕錄制
  • Python+OpenCV+pyQt5錄制雙目攝像頭視頻的實例
  • python基于tkinter實現(xiàn)gif錄屏功能

標簽:宿遷 黃山 景德鎮(zhèn) 臺灣 濟南 三沙 喀什 欽州

巨人網(wǎng)絡通訊聲明:本文標題《python實現(xiàn)的B站直播錄制工具》,本文關鍵詞  python,實現(xiàn),的,站,直播,錄制,;如發(fā)現(xiàn)本文內容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《python實現(xiàn)的B站直播錄制工具》相關的同類信息!
  • 本頁收集關于python實現(xiàn)的B站直播錄制工具的相關信息資訊供網(wǎng)民參考!
  • 推薦文章
    池州市| 论坛| 卢氏县| 岳阳市| 兴和县| 华安县| 伊宁县| 汉川市| 嵊泗县| 五华县| 临湘市| 扬中市| 津南区| 临朐县| 海宁市| 化州市| 峨眉山市| 阳曲县| 玉田县| 肇州县| 徐闻县| 南靖县| 乌什县| 丹凤县| 沈丘县| 四会市| 中方县| 陆丰市| 六安市| 和林格尔县| 五指山市| 克什克腾旗| 叶城县| 阳新县| 武隆县| 邻水| 同心县| 绿春县| 渝北区| 饶平县| 九龙坡区|