feat: add hls/flv recording option for douyin and tiktok

This commit is contained in:
ihmily
2025-07-14 10:55:18 +08:00
parent 84568f2d52
commit ee9bad89bf
5 changed files with 65 additions and 35 deletions

View File

@@ -140,9 +140,23 @@ class LiveStreamRecorder:
cleaned_title = title[:30].replace("", ",").replace(" ", "")
return cleaned_title
def _get_record_url(self, url: str):
http_record_list = ["shopee", "migu"]
@property
def is_flv_preferred_platform(self):
return self.platform_key in {"douyin", "tiktok"}
def _select_source_url(self, stream_info: StreamData):
if (
self.user_config.get("default_live_source") != "HLS"
and self.is_flv_preferred_platform
and self.quality not in {"OD", 0}
):
return stream_info.flv_url
return stream_info.record_url
def _get_record_url(self, stream_info: StreamData):
url = self._select_source_url(stream_info)
http_record_list = ["shopee", "migu"]
if self.user_config.get("force_https_recording") and url.startswith("http://"):
url = url.replace("http://", "https://")
@@ -190,7 +204,7 @@ class LiveStreamRecorder:
logger.info(f"Save Path: {save_path}")
self.recording.recording_dir = os.path.dirname(save_path)
os.makedirs(self.recording.recording_dir, exist_ok=True)
record_url = self._get_record_url(stream_info.record_url)
record_url = self._get_record_url(stream_info)
if use_direct_download:
logger.info(f"Use Direct Downloader to Download FLV Stream: {record_url}")
@@ -199,14 +213,14 @@ class LiveStreamRecorder:
if header_params:
key, value = header_params.split(":", 1)
headers[key] = value
self.direct_downloader = DirectStreamDownloader(
record_url=record_url,
save_path=save_path,
headers=headers,
proxy=self.proxy
)
self.app.page.run_task(
self.start_direct_download,
stream_info.anchor_name,
@@ -231,20 +245,20 @@ class LiveStreamRecorder:
self.start_ffmpeg,
stream_info.anchor_name,
self.live_url,
stream_info.record_url,
record_url,
ffmpeg_command,
self.save_format,
self.user_config.get("custom_script_command")
)
async def start_ffmpeg(
self,
record_name: str,
live_url: str,
record_url: str,
ffmpeg_command: list,
save_type: str,
script_command: str | None = None
self,
record_name: str,
live_url: str,
record_url: str,
ffmpeg_command: list,
save_type: str,
script_command: str | None = None
) -> bool:
"""
The child process executes ffmpeg for recording
@@ -494,13 +508,13 @@ class LiveStreamRecorder:
logger.error(f"An unknown error occurred: {e}")
async def custom_script_execute(
self,
script_command: str,
record_name: str,
save_file_path: str,
save_type: str,
split_video_by_time: bool,
converts_to_mp4: bool
self,
script_command: str,
record_name: str,
save_file_path: str,
save_type: str,
split_video_by_time: bool,
converts_to_mp4: bool
):
from ..process_manager import BackgroundService
@@ -585,36 +599,36 @@ class LiveStreamRecorder:
return record_headers.get(platform_key)
async def start_direct_download(
self,
record_name: str,
live_url: str,
record_url: str,
save_file_path: str,
save_type: str,
script_command: str | None = None
self,
record_name: str,
live_url: str,
record_url: str,
save_file_path: str,
save_type: str,
script_command: str | None = None
) -> bool:
"""
Use the direct downloader to download the live stream
"""
try:
await self.direct_downloader.start_download()
self.recording.status_info = RecordingStatus.RECORDING
self.recording.record_url = record_url
logger.info(f"Direct Downloading: {live_url}")
logger.log("STREAM", f"Direct Download Stream URL: {record_url}")
while True:
if not self.recording.is_recording or not self.app.recording_enabled:
logger.info(f"Prepare to end direct download: {live_url}")
await self.direct_downloader.stop_download()
break
await asyncio.sleep(1)
if self.direct_downloader.download_task and self.direct_downloader.download_task.done():
break
if self.recording.monitor_status:
self.recording.status_info = RecordingStatus.MONITORING
display_title = self.recording.title
@@ -649,7 +663,7 @@ class LiveStreamRecorder:
self.recording.notified_live_end = True
self.recording.is_recording = False
try:
self.recording.update({"display_title": display_title})
await self.app.record_card_manager.update_card(self.recording)
@@ -686,7 +700,7 @@ class LiveStreamRecorder:
)
return True
except Exception as e:
logger.error(f"Error occurred during direct download: {e}")
self.recording.status_info = RecordingStatus.RECORDING_ERROR
@@ -702,4 +716,4 @@ class LiveStreamRecorder:
logger.debug(f"Failed to update UI: {e}")
return False
finally:
self.recording.record_url = None
self.recording.record_url = None

View File

@@ -364,6 +364,17 @@ class SettingsPage(PageBase):
on_change=self.on_change,
),
),
self.create_setting_row(
self._["default_live_source"],
ft.Dropdown(
options=[ft.dropdown.Option(i) for i in ['HLS', 'FLV']],
value=self.get_config_value("default_live_source", 'FLV'),
width=200,
data="default_live_source",
on_change=self.on_change,
tooltip=self._["default_live_source_tip"],
),
),
self.create_setting_row(
self._["space_threshold"],
ft.TextField(

View File

@@ -14,6 +14,7 @@
"loop_time_seconds": "180",
"segmented_recording_enabled": true,
"force_https_recording": true,
"default_live_source": "FLV",
"recording_space_threshold": "2.0",
"video_segment_time": "1800",
"convert_to_mp4": true,

View File

@@ -217,6 +217,8 @@
"loop_time": "Loop Time (Seconds)",
"is_segmented_recording_enabled": "Enable Segmented Recording",
"force_https": "Force HTTPS Recording",
"default_live_source": "Default Live Source",
"default_live_source_tip": "This setting only works for Douyin and TikTok platforms",
"space_threshold": "Remaining Space Threshold (GB) for Recording",
"segment_time": "Video Segment Time (Seconds)",
"convert_mp4": "Convert to MP4 After Recording",

View File

@@ -219,6 +219,8 @@
"loop_time": "循环时间(秒)",
"is_segmented_recording_enabled": "分段录制是否开启",
"force_https": "强制启用https录制",
"default_live_source": "默认直播源",
"default_live_source_tip": "该设置只对抖音和TikTok平台有效",
"space_threshold": "录制空间剩余阈值(gb)",
"segment_time": "视频分段时间(秒)",
"convert_mp4": "录制完成后转为mp4格式",