Quy trình ghi hình trực tiếp từ Douyu chủ yếu dựa trên spider.py, stream.py và main.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(' ', '')
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áishow_statuscó 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ố
ratexác định độ phân giải: 0: 4K3: 1080p2: 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ấyrtmp_urlvàrtmp_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đọcflv_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
- Lấy thông tin phòng trực tiếp →
spider.get_douyu_info_data() - Lấy URL luồng trực tiếp →
spider.get_douyu_stream_data() - Xử lý URL cuối cùng →
stream.get_douyu_stream_url() - Ghi hình bằng
ffmpeg→start_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.