Phân tích quy trình ghi hình trực tiếp từ Douyu

Quy trình ghi hình trực tiếp từ Douyu chủ yếu dựa trên spider.py, stream.pymain.py. Dưới đây là cách mã nguồn thu thập và lưu trữ luồng trực tiếp từ nền tảng này:

1. Lấy thông tin phòng trực tiếp

Trong spider.py, hàm get_douyu_info_data(url, proxy_addr, cookies) được sử dụng để lấy thông tin cơ bản của phòng trực tiếp:

@trace_error_decorator
async def get_douyu_info_data(url: str, proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
    headers = {
        'User-Agent': 'ios/7.830 (ios 17.0; ; iPhone 15 ...)',
        'Referer': 'https://m.douyu.com/...',
        'Cookie': 'dy_did=413b835d2ae00270f0c69f6400031601; ...'
    }
    if cookies:
        headers['Cookie'] = cookies

    match_rid = re.search('rid=(.*?)(?=&|$)', url)
    if match_rid:
        rid = match_rid.group(1)
    else:
        rid = re.search('douyu.com/(.*?)(?=\\?|$)', url).group(1)
        html_str = await async_req(url=f'https://m.douyu.com/{rid}', proxy_addr=proxy_addr, headers=headers)
        json_str = re.findall('<script id="vike_pageContext" type="application/json">(.*?)</script>', html_str)[0]
        json_data = json.loads(json_str)
        rid = json_data['pageProps']['room']['roomInfo']['roomInfo']['rid']

    headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; ...)'
    url2 = f'https://www.douyu.com/betard/{rid}'
    json_str = await async_req(url=url2, proxy_addr=proxy_addr, headers=headers)
    json_data = json.loads(json_str)

    result = {
        "anchor_name": json_data['room']['nickname'],
        "is_live": False
    }
    if json_data['room']['videoLoop'] == 0 and json_data['room']['show_status'] == 1:
        result["title"] = json_data['room']['room_name'].replace('&nbsp;', '')
        result["is_live"] = True
        result["room_id"] = json_data['room']['room_id']
    return result

Giải thích

  • Truy cập https://m.douyu.com/{room_id} để lấy thông tin cơ bản.
  • Phân tích để lấy room_id, kiểm tra trạng thái show_status có phải là 1 (đang phát trực tiếp) hay không.
  • Trả về tên người livestream (anchor_name), tiêu đề phòng (title) và trạng thái phát trực tiếp (is_live).

2. Lấy URL luồng trực tiếp

Trong spider.py, hàm get_douyu_stream_data(rid, rate, proxy_addr, cookies) được dùng để trích xuất URL luồng:

@trace_error_decorator
async def get_douyu_stream_data(rid: str, rate: str = '-1', proxy_addr: OptionalStr = None, cookies: OptionalStr = None) -> dict:
    did = '10000000000000000000000000003306'
    params_list = await get_token_js(rid, did, proxy_addr=proxy_addr)
    headers = {
        'User-Agent': 'ios/7.830 (ios 17.0; ...)',
        'Referer': 'https://m.douyu.com/...',
        'Cookie': 'dy_did=413b835d2ae00270f0c69f6400031601; ...'
    }
    if cookies:
        headers['Cookie'] = cookies

    data = {
        'v': params_list[0],
        'did': params_list[1],
        'tt': params_list[2],
        'sign': params_list[3],
        'ver': '22011191',
        'rid': rid,
        'rate': rate,  # 0: 4K, 3: 1080p, 2: 720p, -1: mặc định
    }

    app_api = f'https://www.douyu.com/lapi/live/getH5Play/{rid}'
    json_str = await async_req(url=app_api, proxy_addr=proxy_addr, headers=headers, data=data)
    json_data = json.loads(json_str)
    return json_data

Giải thích

  • Tạo params_list để xác thực.
  • Gọi API https://www.douyu.com/lapi/live/getH5Play/{rid} để lấy dữ liệu luồng.
  • Tham số rate xác định độ phân giải:
  • 0: 4K
  • 3: 1080p
  • 2: 720p
  • -1: mặc định

3. Xử lý URL luồng trực tiếp

Trong stream.py, hàm get_douyu_stream_url(json_data, video_quality, cookies, proxy_addr) tạo URL cuối cùng để ghi hình:

@trace_error_decorator
async def get_douyu_stream_url(json_data: dict, video_quality: str, cookies: str, proxy_addr: str) -> dict:
    if not json_data["is_live"]:
        return json_data

    video_quality_options = {
        "OD": '0',
        "BD": '0',
        "UHD": '3',
        "HD": '2',
        "SD": '1',
        "LD": '1'
    }

    rid = str(json_data["room_id"])
    json_data.pop("room_id")
    rate = video_quality_options.get(video_quality, '0')
    flv_data = await get_douyu_stream_data(rid, rate, cookies=cookies, proxy_addr=proxy_addr)
    rtmp_url = flv_data['data'].get('rtmp_url')
    rtmp_live = flv_data['data'].get('rtmp_live')

    if rtmp_live:
        flv_url = f'{rtmp_url}/{rtmp_live}'
        json_data |= {'quality': video_quality, 'flv_url': flv_url, 'record_url': flv_url}
    return json_data

Giải thích

  • Truyền video_quality để chọn độ phân giải.
  • Gọi get_douyu_stream_data để lấy rtmp_urlrtmp_live.
  • Tạo URL flv_url để ghi hình.

4. Ghi hình luồng trực tiếp

Trong main.py, hàm start_record(url_data: tuple, count_variable: int = -1) thực hiện quá trình ghi hình:

elif record_url.find("https://www.douyu.com/") > -1:
    platform = 'Douyu'
    with semaphore:
        json_data = asyncio.run(spider.get_douyu_info_data(
            url=record_url, proxy_addr=proxy_address, cookies=douyu_cookie))
        port_info = asyncio.run(stream.get_douyu_stream_url(
            json_data, video_quality=record_quality, cookies=douyu_cookie, proxy_addr=proxy_address
        ))

Sau đó gọi ffmpeg để ghi hình:

ffmpeg_command = [
    'ffmpeg', "-y",
    "-v", "verbose",
    "-rw_timeout", "15000000",
    "-loglevel", "error",
    "-hide_banner",
    "-user_agent", user_agent,
    "-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp,httpproxy",
    "-thread_queue_size", "1024",
    "-analyzeduration", analyzeduration,
    "-probesize", probesize,
    "-fflags", "+discardcorrupt",
    "-re", "-i", real_url,
    "-bufsize", bufsize,
    "-sn", "-dn",
    "-reconnect_delay_max", "60",
    "-reconnect_streamed", "-reconnect_at_eof",
    "-max_muxing_queue_size", max_muxing_queue_size,
    "-correct_ts_overflow", "1",
    "-avoid_negative_ts", "1"
]

Giải thích

  • ffmpeg đọc flv_url để ghi hình.
  • Tùy chọn -re đảm bảo ghi hình theo thời gian thực, tránh gián đoạn do tốc độ đọc quá nhanh.

Tóm tắt

  1. Lấy thông tin phòng trực tiếpspider.get_douyu_info_data()
  2. Lấy URL luồng trực tiếpspider.get_douyu_stream_data()
  3. Xử lý URL cuối cùngstream.get_douyu_stream_url()
  4. Ghi hình bằng ffmpegstart_record()

Quy trình này đảm bảo tính ổn định và hiệu quả khi ghi hình trực tiếp từ Douyu.

Thẻ: Douyu live streaming ffmpeg python API

Đăng vào ngày 12 tháng 6 lúc 02:33