mirror of
https://github.com/ihmily/StreamCap.git
synced 2026-06-04 04:49:44 +08:00
feat: add hls/flv recording option for douyin and tiktok
This commit is contained in:
@@ -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
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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格式",
|
||||
|
||||
Reference in New Issue
Block a user