From 36434d67c72f355423d2f393f90fafb722207510 Mon Sep 17 00:00:00 2001 From: jlvihv Date: Mon, 17 Jun 2024 15:08:59 +0800 Subject: [PATCH] http proxy --- bun.lockb | Bin 141718 -> 142136 bytes package.json | 99 +++---- src-tauri/Cargo.lock | 285 ++++++++++++++++++--- src-tauri/Cargo.toml | 4 +- src-tauri/capabilities/default.json | 4 +- src-tauri/src/ffmpeg.rs | 36 ++- src-tauri/src/lib.rs | 2 + src-tauri/src/manager.rs | 19 +- src-tauri/src/model.rs | 19 +- src-tauri/tauri.conf.json | 2 +- src/lib/components/CollapsiblePanel.svelte | 3 +- src/lib/i18n/cn.json | 8 +- src/lib/i18n/en.json | 8 +- src/lib/model.ts | 4 + src/lib/platform/douyin.ts | 3 +- src/lib/platform/huya.ts | 3 +- src/lib/platform/tiktok.ts | 3 +- src/lib/platform/twitch.ts | 1 + src/lib/platform/xiaohongshu.ts | 17 +- src/routes/config/+page.svelte | 76 +++--- src/routes/live/forward/+page.svelte | 4 +- src/routes/record/+page.svelte | 92 +++++-- src/routes/record/history/+page.svelte | 78 +++--- src/routes/record/plan/+page.svelte | 62 ++--- 24 files changed, 574 insertions(+), 258 deletions(-) diff --git a/bun.lockb b/bun.lockb index 54ee432931bf98cd453975df5bd89d899f7c936b..f28aa373217ee6a4dce11622ff2dfb30cb1ea82b 100755 GIT binary patch delta 23670 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 QGeq+P%u-nU0LxAIFMGwUQ2+n{ delta 23617 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 t0) -> Result { @@ -54,8 +54,16 @@ pub fn execute_ffmpeg_command_return_output(ffmpeg_command: Vec) -> Resu Ok(stdout.to_string()) } -pub fn record(ffmpeg_path: &str, url: &str, filename: &str) -> Result { - println!("开始录制:{} -> {}", url, filename); +pub fn record( + ffmpeg_path: &str, + url: &str, + filename: &str, + option: Option, +) -> Result { + println!( + "开始录制:{} -> {}, recording option: {:?}", + url, filename, option + ); // 调用 ffmpeg 命令 let mut cmd = std::process::Command::new(ffmpeg_path); @@ -66,7 +74,11 @@ pub fn record(ffmpeg_path: &str, url: &str, filename: &str) -> Result { cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW } cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - let ffmpeg_command = build_ffmpeg_record_command(url, filename, None); + let ffmpeg_command = build_ffmpeg_record_command( + url, + filename, + option.map(|o| o.use_proxy).unwrap_or_default(), + ); cmd.args(&ffmpeg_command); let mut child = cmd.spawn()?; // 立刻 try_wait 一下,看是否有错误 @@ -95,8 +107,12 @@ fn build_ffmpeg_record_command(url: &str, filename: &str, proxy: Option) let probesize = "10000000"; let bufsize = "8000k"; let max_muxing_queue_size = "1024"; - - let mut ffmpeg_command = vec![ + let mut ffmpeg_command = vec![]; + if let Some(proxy) = &proxy { + println!("proxy: {}", proxy); + ffmpeg_command.extend_from_slice(&["-http_proxy", proxy.as_str()] as &[&str]); + } + let record_command = vec![ "-y", "-v", "verbose", @@ -108,7 +124,7 @@ fn build_ffmpeg_record_command(url: &str, filename: &str, proxy: Option) "-user_agent", user_agent, "-protocol_whitelist", - "rtmp,crypto,file,http,https,tcp,tls,udp,rtp", + "rtmp,crypto,file,http,https,tcp,tls,udp,rtp,httpproxy", "-thread_queue_size", "1024", "-analyzeduration", @@ -132,12 +148,10 @@ fn build_ffmpeg_record_command(url: &str, filename: &str, proxy: Option) "-correct_ts_overflow", "1", ]; - if let Some(proxy) = &proxy { - ffmpeg_command.extend_from_slice(&["-http_proxy", proxy.as_str()] as &[&str]); - } let push_command = [ "-c:v", "copy", "-c:a", "copy", "-map", "0", "-f", "mpegts", filename, ]; + ffmpeg_command.extend_from_slice(&record_command); ffmpeg_command.extend_from_slice(&push_command); ffmpeg_command.into_iter().map(|s| s.into()).collect() } @@ -239,7 +253,7 @@ mod tests { async fn test_record() { let url = "http://pull-hls-l13.douyincdn.com/stage/stream-691574246930121144_or4.m3u8?expire=1715939773&sign=f73837f8a9bac9cac894a331e8a621cf"; let filename = "test.ts"; - let child = record("ffmpeg", url, filename).unwrap(); + let child = record("ffmpeg", url, filename, None).unwrap(); let output = child.wait_with_output().unwrap(); let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9f641a4..1ebee2e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -13,6 +13,7 @@ mod utils; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_dialog::init()) .setup(|app| { @@ -57,6 +58,7 @@ pub fn run() { manager::request_api::try_request_get_status, manager::request_api::request_post, manager::my_utils::get_youtube_info, + manager::my_utils::get_system_proxy_info, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/manager.rs b/src-tauri/src/manager.rs index f030411..3b8e0bc 100644 --- a/src-tauri/src/manager.rs +++ b/src-tauri/src/manager.rs @@ -1,5 +1,6 @@ use crate::model::AppConfig; use crate::model::LiveInfo; +use crate::model::RecordingOption; use crate::model::{PlatformKind, Stream}; use crate::{ ffmpeg, kv, @@ -23,6 +24,7 @@ pub mod inner { pub async fn start_record_with_stream( stream: Stream, live_info: LiveInfo, + option: Option, ) -> anyhow::Result<()> { // 如果已经在录制了,就不再录制,返回错误 if inner::get_record_status(&live_info.url).await? == RecordStatus::Recording { @@ -43,7 +45,7 @@ pub mod inner { .to_str() .ok_or_else(|| anyhow::anyhow!("Could not convert path to string: {:?}", path))?; - inner::record_with_ffmpeg(&live_info.url, &stream.url, full_filename).await?; + inner::record_with_ffmpeg(&live_info.url, &stream.url, full_filename, option).await?; // 记录录制历史 let mut history = RecordingHistory::new(&live_info.url, full_filename); @@ -59,9 +61,10 @@ pub mod inner { url: &str, stream_url: &str, full_filename: &str, + option: Option, ) -> anyhow::Result<()> { let ffmpeg_path = kv::config::get()?.ffmpeg_path; - let mut child = match ffmpeg::record(&ffmpeg_path, stream_url, full_filename) { + let mut child = match ffmpeg::record(&ffmpeg_path, stream_url, full_filename, option) { Ok(child) => child, Err(e) => { eprintln!("Could not start recording: {}", e); @@ -107,6 +110,7 @@ pub mod record { auto_record: bool, stream: Stream, live_info: LiveInfo, + option: Option, ) -> Result { // 如果要自动录制,加入录制计划表 if auto_record { @@ -114,6 +118,7 @@ pub mod record { &live_info.url, stream.protocol.clone(), stream.resolution.clone(), + option.clone(), ); plan.live_info = Some(live_info.clone()); kv::plan::add(&plan).map_err(|e| format!("Could not add recording plan: {}", e))?; @@ -123,7 +128,7 @@ pub mod record { eprintln!("Could not add live info: {}", e); e.to_string() })?; - inner::start_record_with_stream(stream, live_info) + inner::start_record_with_stream(stream, live_info, option) .await .map_err(|e| { eprintln!("Could not start recording: {}", e); @@ -497,4 +502,12 @@ pub mod my_utils { .map_err(|e| format!("Could not get video info: {}", e))?; Ok(video_info) } + + #[tauri::command] + pub async fn get_system_proxy_info() -> Result { + // 读取环境变量中的 http_proxy 或 HTTP_PROXY 或 all_proxy 或 ALL_PROXY + let proxy = sysproxy::Sysproxy::get_http() + .map_err(|e| format!("can not get system http proxy: {}", e.to_string()))?; + Ok(format!("http://{}:{}", proxy.host, proxy.port)) + } } diff --git a/src-tauri/src/model.rs b/src-tauri/src/model.rs index 39776c7..a5c71ce 100644 --- a/src-tauri/src/model.rs +++ b/src-tauri/src/model.rs @@ -169,6 +169,21 @@ pub struct RecordingPlan { pub updated_at: i64, // 直播间信息 pub live_info: Option, + // 录制选项 + #[serde(default)] + pub option: RecordingOption, +} + +// 录制选项 +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +#[serde(rename_all = "camelCase", default)] +pub struct RecordingOption { + // 使用代理 + pub use_proxy: Option, + // 自动转 mp4 + pub auto_convert_to_mp4: bool, + // 删除原文件 + pub delete_original_file: bool, } // 录制策略 @@ -246,6 +261,7 @@ pub mod plan { url: &str, stream_protocol: StreamingProtocol, stream_resolution: String, + option: Option, ) -> RecordingPlan { RecordingPlan { url: url.into(), @@ -256,11 +272,12 @@ pub mod plan { live_info: None, stream_protocol, stream_resolution, + option: option.unwrap_or_default(), } } pub fn new_with_url(url: &str) -> RecordingPlan { - RecordingPlan::new(url, StreamingProtocol::Flv, "".into()) + RecordingPlan::new(url, StreamingProtocol::Flv, "".into(), None) } } } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 7c97ee1..362e24f 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "liveship", - "version": "0.1.19", + "version": "0.1.20", "identifier": "app.happyship.liveship", "build": { "beforeDevCommand": "bun run dev", diff --git a/src/lib/components/CollapsiblePanel.svelte b/src/lib/components/CollapsiblePanel.svelte index 9cf538d..b44a69e 100644 --- a/src/lib/components/CollapsiblePanel.svelte +++ b/src/lib/components/CollapsiblePanel.svelte @@ -1,4 +1,5 @@ @@ -104,7 +112,7 @@ @@ -123,7 +131,7 @@ diff --git a/src/routes/live/forward/+page.svelte b/src/routes/live/forward/+page.svelte index 942919d..1e9d45e 100644 --- a/src/routes/live/forward/+page.svelte +++ b/src/routes/live/forward/+page.svelte @@ -66,7 +66,7 @@ } let ffmpegCommand = buildFFmpegForwardCommand(streamUrl, outputUrl); try { - invoke('execute_ffmpeg_command', { ffmpegCommand }); + await invoke('execute_ffmpeg_command', { ffmpegCommand }); // 只要不异常,就认为成功了 forwarding = true; } catch (e) { @@ -77,7 +77,7 @@ async function stopForward() { try { - invoke('stop_ffmpeg_command'); + await invoke('stop_ffmpeg_command'); forwarding = false; } catch (e) { toast.error(e as string); diff --git a/src/routes/record/+page.svelte b/src/routes/record/+page.svelte index 2181767..39596ae 100644 --- a/src/routes/record/+page.svelte +++ b/src/routes/record/+page.svelte @@ -17,7 +17,8 @@ type LiveInfo, type Stream, type RecordingPlan, - StreamingProtocol + StreamingProtocol, + type RecordingOption } from '$lib/model'; import { t } from '@/translations'; import Button from '$lib/components/button.svelte'; @@ -35,6 +36,8 @@ let streamUrl = $state(''); let errorMessage = $state(''); let inPlan = $state(false); + let useProxy = $state(false); + let recordingOption: RecordingOption = $state({ useProxy: null }); let queryHistory: { url: string; anchorName: string; platformKind: PlatformKind }[] = $state([]); const tryLinks: string[] = [ 'https://live.douyin.com/790601393533', @@ -52,7 +55,7 @@ let requesting = $state(false); // 表示是否正在请求 live info let advancedOptions = $state(false); - onMount(() => { + onMount(async () => { // 组件挂载时,设置 visible 为 true,以触发过渡动画 visible = true; @@ -60,7 +63,7 @@ // 从 localStorage 中获取 ffmpegDownloading,如果正在下载中,就不用管了 if (localStorage.getItem('ffmpegDownloading') !== 'true') { try { - invoke('check_ffmpeg_availability'); + await invoke('check_ffmpeg_availability'); // 如果已经安装了 ffmpeg,什么也不做 } catch (e) { // 没安装 ffmpeg,弹出对话框 @@ -73,6 +76,14 @@ if (history) { queryHistory = JSON.parse(history); } + + // 获取系统代理 + try { + let proxy = await invoke('get_system_proxy_info'); + console.log('proxy: ', proxy); + } catch (e) { + console.error('get system proxy error: ', e); + } }); // 防抖调用 api, 500ms 内只调用一次 @@ -129,6 +140,23 @@ } } + async function handleUseProxyChange(event: Event) { + try { + await tick(); + if (event.target === null) { + throw new Error('Event target is null'); + } + const checked = (event.target as HTMLInputElement).checked; + if (checked) { + recordingOption.useProxy = await invoke('get_system_proxy_info'); + } else { + recordingOption.useProxy = null; + } + } catch (e) { + console.error('handle use proxy change error: ', e); + } + } + async function handleAddPlan(enabled: boolean) { if (url == '') { toast.error($t('pleaseInputLiveAddress')); @@ -148,7 +176,7 @@ createdAt: new Date().getTime(), updatedAt: new Date().getTime() }; - invoke('add_plan', { plan }); + await invoke('add_plan', { plan }); if (enabled) { toast.success($t('planEnabled')); } else { @@ -165,8 +193,7 @@ // 获取对应 url 的计划信息 async function getPlan(): Promise { try { - let data = await invoke('get_plan', { url }); - let plan = data as RecordingPlan; + let plan: RecordingPlan = await invoke('get_plan', { url }); return plan; } catch (e) { return null; @@ -196,7 +223,8 @@ recordStatus = await invoke('start_record', { autoRecord, stream, - liveInfo: liveInfo! + liveInfo: liveInfo!, + option: recordingOption }); toast.success($t('recordAlreadyStarted')); await isInPlan(); @@ -233,7 +261,7 @@ // 写入 localStorage,表示正在下载中 localStorage.setItem('ffmpegDownloading', 'true'); try { - invoke('download_ffmpeg'); + await invoke('download_ffmpeg'); toast.success($t('downloadedFFmpeg')); } catch (e) { toast.error($t('downloadFFmpegFailed'), { @@ -277,7 +305,7 @@ {#if visible}
{inPlan ? $t('inPlan') : $t('notInPlan')} {/if}
- + > --> + + + {/if}
{#if loading} @@ -482,7 +532,7 @@ {/if}
{:else} -
+
{#if queryHistory.length <= 5}

{$t('tryThese')}

diff --git a/src/routes/record/history/+page.svelte b/src/routes/record/history/+page.svelte index af8ebf0..8b9571c 100644 --- a/src/routes/record/history/+page.svelte +++ b/src/routes/record/history/+page.svelte @@ -47,31 +47,28 @@ }); async function getAllHistory() { - invoke('get_all_history') - .then((data) => { - list = data as RecordingHistory[]; - }) - .catch((e) => { - toast.error($t('getRecordHistoryFailed'), { - description: e - }); + try { + list = await invoke('get_all_history'); + } catch (e) { + toast.error($t('getRecordHistoryFailed'), { + description: e as string }); + } } // 删除一条历史记录 async function deleteHistory(url: string, startTime: number, deleteFile: boolean) { closeDialog(deleteHistoryDialogId); deleteHistoryParams = undefined; - invoke('delete_history', { url, startTime, deleteFile }) - .then(() => { - toast.success($t('deleteSuccess')); - getAllHistory(); - }) - .catch((e) => { - toast.error($t('deleteSuccess'), { - description: e - }); + try { + await invoke('delete_history', { url, startTime, deleteFile }); + toast.success($t('deleteSuccess')); + await getAllHistory(); + } catch (e) { + toast.error($t('deleteFailed'), { + description: e as string }); + } } async function stopRecord(url: string, disablePlan: boolean) { @@ -81,39 +78,36 @@ toast.error($t('urlCannotBeEmpty')); return; } - if (disablePlan) { + try { // 禁用此计划 - invoke('update_plan_status', { url, enabled: false }) - .then(() => {}) - .catch((e) => { - toast.error($t('disablePlanFailed'), { - description: e - }); - }); + await invoke('update_plan_status', { url, enabled: false }); + } catch (e) { + toast.error($t('disablePlanFailed'), { + description: e as string + }); } - invoke('stop_record', { url }) - .then(() => { - toast.success($t('recordAlreadyStopped')); - getAllHistory(); - }) - .catch((e) => { - toast.error($t('recordStopFailed'), { - description: e - }); + + try { + await invoke('stop_record', { url }); + toast.success($t('recordAlreadyStopped')); + await getAllHistory(); + } catch (e) { + toast.error($t('recordStopFailed'), { + description: e as string }); + } } // 在文件管理器中打开文件夹 async function openInFolder(path: string) { - invoke('open_in_folder', { path }) - .then(() => { - toast.success($t('openedInFileManager')); - }) - .catch((e) => { - toast.error($t('openInFileManagerFailed'), { - description: e - }); + try { + await invoke('open_in_folder', { path }); + toast.success($t('openedInFileManager')); + } catch (e) { + toast.error($t('openInFileManagerFailed'), { + description: e as string }); + } } // 计算录制时长,传入开始时间和结束时间,结束时间如果为 0, 则计算到当前时间 diff --git a/src/routes/record/plan/+page.svelte b/src/routes/record/plan/+page.svelte index 8a7ab3d..a373e66 100644 --- a/src/routes/record/plan/+page.svelte +++ b/src/routes/record/plan/+page.svelte @@ -31,56 +31,50 @@ }); async function getAllPlans() { - invoke('get_all_plans') - .then((data) => { - list = data as RecordingPlan[]; - }) - .catch((e) => { - toast.error($t('getRecordPlanFailed'), { - description: e - }); + try { + list = await invoke('get_all_plans'); + } catch (e) { + toast.error($t('getRecordPlanFailed'), { + description: e as string }); + } } async function deletePlan(url: string) { closeDialog(deletePlanDialogId); dialogUrl = ''; - invoke('delete_plan', { url }) - .then(() => { - toast.success($t('deleteSuccess')); - getAllPlans(); - }) - .catch((e) => { - toast.error($t('deleteFailed'), { - description: e - }); + try { + await invoke('delete_plan', { url }); + toast.success($t('deleteSuccess')); + await getAllPlans(); + } catch (e) { + toast.error($t('deleteFailed'), { + description: e as string }); + } } async function updatePlanStatus(url: string, enabled: boolean) { - invoke('update_plan_status', { url, enabled }) - .then(() => { - toast.success($t('updateSuccess')); - getAllPlans(); - }) - .catch((e) => { - toast.error($t('updateFailed'), { - description: e - }); + try { + await invoke('update_plan_status', { url, enabled }); + toast.success($t('updateSuccess')); + await getAllPlans(); + } catch (e) { + toast.error($t('updateFailed'), { + description: e as string }); + } } // 获取最新一次轮询时间 async function getLastPollingTime() { - invoke('get_last_polling_time') - .then((data) => { - lastPollingTime = data as number; - }) - .catch((e) => { - toast.error($t('getLatestPollTimeFailed'), { - description: e - }); + try { + lastPollingTime = await invoke('get_last_polling_time'); + } catch (e) { + toast.error($t('getLatestPollTimeFailed'), { + description: e as string }); + } }