diff --git a/Cargo.lock b/Cargo.lock index bf3ce1f04..237369d2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1533,7 +1533,7 @@ dependencies = [ "log", "objc", "pkg-config", - "rdev", + "rdev 0.5.0-2 (git+https://github.com/asur4s/rdev)", "serde 1.0.147", "serde_derive", "tfc", @@ -4248,6 +4248,28 @@ dependencies = [ "x11 2.20.0", ] +[[package]] +name = "rdev" +version = "0.5.0-2" +source = "git+https://github.com/rustdesk/rdev#25c29f61bfdf5d8ec50f0a8a7743bc1d85eb2c04" +dependencies = [ + "cocoa", + "core-foundation 0.9.3", + "core-foundation-sys 0.8.3", + "core-graphics 0.22.3", + "enum-map", + "epoll", + "inotify", + "lazy_static", + "libc", + "mio 0.8.5", + "strum 0.24.1", + "strum_macros 0.24.3", + "widestring 1.0.2", + "winapi 0.3.9", + "x11 2.20.0", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -4518,7 +4540,7 @@ dependencies = [ "num_cpus", "objc", "parity-tokio-ipc", - "rdev", + "rdev 0.5.0-2 (git+https://github.com/rustdesk/rdev)", "repng", "reqwest", "rpassword 7.1.0", diff --git a/Cargo.toml b/Cargo.toml index 2861b3f63..a783b1abe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ default-net = "0.11.0" wol-rs = "0.9.1" flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true } errno = "0.2.8" -rdev = { git = "https://github.com/asur4s/rdev" } +rdev = { git = "https://github.com/rustdesk/rdev" } url = { version = "2.1", features = ["serde"] } reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } @@ -162,4 +162,4 @@ codegen-units = 1 panic = 'abort' strip = true #opt-level = 'z' # only have smaller size after strip -rpath = true \ No newline at end of file +rpath = true diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index b9077b0cb..0f5502f54 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1013,7 +1013,7 @@ class LastWindowPosition { return LastWindowPosition(m["width"], m["height"], m["offsetWidth"], m["offsetHeight"], m["isMaximized"]); } catch (e) { - debugPrint(e.toString()); + debugPrintStack(label: e.toString()); return null; } } @@ -1147,7 +1147,7 @@ Future restoreWindowPosition(WindowType type, {int? windowId}) async { final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name); var lpos = LastWindowPosition.loadFromString(pos); if (lpos == null) { - debugPrint("window position saved, but cannot be parsed"); + debugPrint("no window position saved, ignoring position restoration"); return false; } @@ -1212,7 +1212,7 @@ Future initUniLinks() async { } parseRustdeskUri(initialLink); } catch (err) { - debugPrint("$err"); + debugPrintStack(label: "$err"); } } @@ -1422,7 +1422,7 @@ void onActiveWindowChanged() async { rustDeskWinManager.closeAllSubWindows() ]); } catch (err) { - debugPrint("$err"); + debugPrintStack(label: "$err"); } finally { await windowManager.setPreventClose(false); await windowManager.close(); diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 5a498a1c4..1711e7b72 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -79,16 +79,19 @@ class _PeerTabPageState extends State .toList() .obs; try { - final json = jsonDecode(bind.getLocalFlutterConfig(k: 'peer-tab-order')); - if (json is List) { - final List list = json.map((e) => e.toString()).toList(); - if (list.length == visibleOrderedTabs.length && - visibleOrderedTabs.every((e) => list.contains(e))) { - visibleOrderedTabs.value = list; + final conf = bind.getLocalFlutterConfig(k: 'peer-tab-order'); + if (conf.isNotEmpty) { + final json = jsonDecode(conf); + if (json is List) { + final List list = json.map((e) => e.toString()).toList(); + if (list.length == visibleOrderedTabs.length && + visibleOrderedTabs.every((e) => list.contains(e))) { + visibleOrderedTabs.value = list; + } } } } catch (e) { - debugPrint('$e'); + debugPrintStack(label: '$e'); } adjustTab(); diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index a51b4d035..06cabebe7 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -63,7 +63,7 @@ class DesktopSettingPage extends StatefulWidget { DesktopTabPage.onAddSetting(initialPage: page); } } catch (e) { - debugPrint('$e'); + debugPrintStack(label: '$e'); } } } diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 794dd1c08..57c7fe4b8 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -31,7 +31,7 @@ class DesktopTabPage extends StatefulWidget { initialPage: initialPage, ))); } catch (e) { - debugPrint('$e'); + debugPrintStack(label: '$e'); } } } diff --git a/flutter/lib/desktop/widgets/login.dart b/flutter/lib/desktop/widgets/login.dart index 3e58a6de2..053653ab3 100644 --- a/flutter/lib/desktop/widgets/login.dart +++ b/flutter/lib/desktop/widgets/login.dart @@ -460,7 +460,7 @@ Future loginDialog() async { debugPrint('$resp'); completer.complete(true); } catch (err) { - debugPrint(err.toString()); + debugPrintStack(label: err.toString()); cancel(); return; } diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 0b2a51d40..38ad18f14 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -564,7 +564,7 @@ void androidChannelInit() { } } } catch (e) { - debugPrint("MethodCallHandler err:$e"); + debugPrintStack(label: "MethodCallHandler err:$e"); } return ""; }); diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 744a3a502..a7968f701 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -363,8 +363,6 @@ class FileModel extends ChangeNotifier { if (_currentRemoteDir.path.isEmpty) { openDirectory(_remoteOption.home, isLocal: false); } - // load last transfer jobs - await bind.sessionLoadLastTransferJobs(id: '${parent.target?.id}'); } Future onClose() async { diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 0a833583e..cf2de4219 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -126,7 +126,7 @@ class PlatformFFI { // no need to set home dir } } catch (e) { - debugPrint('initialize failed: $e'); + debugPrintStack(label: 'initialize failed: $e'); } String id = 'NA'; String name = 'Flutter'; @@ -151,9 +151,8 @@ class PlatformFFI { WindowsDeviceInfo winInfo = await deviceInfo.windowsInfo; name = winInfo.computerName; id = winInfo.computerName; - } catch (e, stacktrace) { - debugPrint("get windows device info failed: $e"); - debugPrintStack(stackTrace: stacktrace); + } catch (e) { + debugPrintStack(label: "get windows device info failed: $e"); name = "unknown"; id = "unknown"; } @@ -174,7 +173,7 @@ class PlatformFFI { await _ffiBind.mainSetHomeDir(home: _homeDir); await _ffiBind.mainInit(appDir: _dir); } catch (e) { - debugPrint('initialize failed: $e'); + debugPrintStack(label: 'initialize failed: $e'); } version = await getVersion(); } diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1a20ed0e1..4c6375dd7 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -43,7 +43,8 @@ pub fn get_display_server() -> String { } fn get_display_server_of_session(session: &str) -> String { - if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "Type", session])) + let mut display_server = if let Ok(output) = + run_loginctl(Some(vec!["show-session", "-p", "Type", session])) // Check session type of the session { let display_server = String::from_utf8_lossy(&output.stdout) @@ -64,28 +65,23 @@ fn get_display_server_of_session(session: &str) -> String { { if xorg_results.trim_end().to_string() != "" { // If it is, manually return "x11", otherwise return tty - "x11".to_owned() - } else { - display_server + return "x11".to_owned(); } - } else { - // If any of these commands fail just fall back to the display server - display_server } - } else { - display_server } - } else { - // loginctl has not given the expected output. try something else. - if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") { - return sestype.to_owned(); - } - // If the session is not a tty, then just return the type as usual - display_server } + display_server } else { "".to_owned() + }; + if display_server.is_empty() { + // loginctl has not given the expected output. try something else. + if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") { + display_server = sestype; + } } + // If the session is not a tty, then just return the type as usual + display_server } pub fn get_values_of_seat0(indices: Vec) -> Vec { @@ -126,8 +122,7 @@ pub fn get_values_of_seat0(indices: Vec) -> Vec { } fn is_active(sid: &str) -> bool { - if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) - { + if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) { String::from_utf8_lossy(&output.stdout).contains("active") } else { false diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 83bd9eee7..9f38f2d6a 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -12,11 +12,10 @@ use hwcodec::mux::{MuxContext, Muxer}; use std::{ fs::{File, OpenOptions}, io, - time::Instant, -}; -use std::{ ops::{Deref, DerefMut}, path::PathBuf, + sync::mpsc::Sender, + time::Instant, }; use webm::mux::{self, Segment, Track, VideoTrack, Writer}; @@ -31,12 +30,14 @@ pub enum RecordCodecID { #[derive(Debug, Clone)] pub struct RecorderContext { + pub server: bool, pub id: String, pub default_dir: String, pub filename: String, pub width: usize, pub height: usize, pub codec_id: RecordCodecID, + pub tx: Option>, } impl RecorderContext { @@ -52,7 +53,8 @@ impl RecorderContext { std::fs::create_dir_all(&dir)?; } } - let file = self.id.clone() + let file = if self.server { "s" } else { "c" }.to_string() + + &self.id.clone() + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() + if self.codec_id == RecordCodecID::VP9 { ".webm" @@ -60,7 +62,7 @@ impl RecorderContext { ".mp4" }; self.filename = PathBuf::from(&dir).join(file).to_string_lossy().to_string(); - log::info!("video save to:{}", self.filename); + log::info!("video will save to:{}", self.filename); Ok(()) } } @@ -75,6 +77,14 @@ pub trait RecorderApi { fn write_video(&mut self, frame: &EncodedVideoFrame) -> bool; } +#[derive(Debug)] +pub enum RecordState { + NewFile(String), + NewFrame, + WriteTail, + RemoveFile, +} + pub struct Recorder { pub inner: Box, ctx: RecorderContext, @@ -110,6 +120,7 @@ impl Recorder { #[cfg(not(feature = "hwcodec"))] _ => bail!("unsupported codec type"), }; + recorder.send_state(RecordState::NewFile(recorder.ctx.filename.clone())); Ok(recorder) } @@ -123,6 +134,7 @@ impl Recorder { _ => bail!("unsupported codec type"), }; self.ctx = ctx; + self.send_state(RecordState::NewFile(self.ctx.filename.clone())); Ok(()) } @@ -171,8 +183,13 @@ impl Recorder { } _ => bail!("unsupported frame type"), } + self.send_state(RecordState::NewFrame); Ok(()) } + + fn send_state(&self, state: RecordState) { + self.ctx.tx.as_ref().map(|tx| tx.send(state)); + } } struct WebmRecorder { @@ -237,9 +254,12 @@ impl RecorderApi for WebmRecorder { impl Drop for WebmRecorder { fn drop(&mut self) { std::mem::replace(&mut self.webm, None).map_or(false, |webm| webm.finalize(None)); + let mut state = RecordState::WriteTail; if !self.written || self.start.elapsed().as_secs() < MIN_SECS { std::fs::remove_file(&self.ctx.filename).ok(); + state = RecordState::RemoveFile; } + self.ctx.tx.as_ref().map(|tx| tx.send(state)); } } @@ -292,8 +312,11 @@ impl RecorderApi for HwRecorder { impl Drop for HwRecorder { fn drop(&mut self) { self.muxer.write_tail().ok(); + let mut state = RecordState::WriteTail; if !self.written || self.start.elapsed().as_secs() < MIN_SECS { std::fs::remove_file(&self.ctx.filename).ok(); + state = RecordState::RemoveFile; } + self.ctx.tx.as_ref().map(|tx| tx.send(state)); } } diff --git a/src/client.rs b/src/client.rs index c646b2b7f..1dd3021b2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -863,12 +863,14 @@ impl VideoHandler { self.record = false; if start { self.recorder = Recorder::new(RecorderContext { + server: false, id, default_dir: crate::ui_interface::default_video_save_directory(), filename: "".to_owned(), width: w as _, height: h as _, codec_id: scrap::record::RecordCodecID::VP9, + tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); } else { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index efeacb61c..326857d3f 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -936,7 +936,7 @@ impl Remote { self.handle_job_status(d.id, d.file_num, err); } Some(file_response::Union::Error(e)) => { - if let Some(job) = fs::get_job(e.id, &mut self.write_jobs) { + if let Some(_job) = fs::get_job(e.id, &mut self.write_jobs) { fs::remove_job(e.id, &mut self.write_jobs); } self.handle_job_status(e.id, e.file_num, Some(e.error)); diff --git a/src/core_main.rs b/src/core_main.rs index bf6ef7aac..b82f45f6a 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -217,7 +217,7 @@ pub fn core_main() -> Option> { if crate::platform::is_root() { crate::ipc::set_permanent_password(args[1].to_owned()).unwrap(); } else { - log::info!("Permission denied!"); + println!("Administrative privileges required!"); } } return None; diff --git a/src/hbbs_http.rs b/src/hbbs_http.rs index ceb3a6081..08ad36eb9 100644 --- a/src/hbbs_http.rs +++ b/src/hbbs_http.rs @@ -4,6 +4,7 @@ use serde_json::{Map, Value}; #[cfg(feature = "flutter")] pub mod account; +pub mod record_upload; #[derive(Debug)] pub enum HbbHttpResponse { diff --git a/src/hbbs_http/record_upload.rs b/src/hbbs_http/record_upload.rs new file mode 100644 index 000000000..93bc745c2 --- /dev/null +++ b/src/hbbs_http/record_upload.rs @@ -0,0 +1,204 @@ +use bytes::Bytes; +use hbb_common::{bail, config::Config, lazy_static, log, ResultType}; +use reqwest::blocking::{Body, Client}; +use scrap::record::RecordState; +use serde::Serialize; +use serde_json::Map; +use std::{ + fs::File, + io::{prelude::*, SeekFrom}, + sync::{mpsc::Receiver, Arc, Mutex}, + time::{Duration, Instant}, +}; + +const MAX_HEADER_LEN: usize = 1024; +const SHOULD_SEND_TIME: Duration = Duration::from_secs(1); +const SHOULD_SEND_SIZE: u64 = 1024 * 1024; + +lazy_static::lazy_static! { + static ref ENABLE: Arc> = Default::default(); +} + +pub fn is_enable() -> bool { + ENABLE.lock().unwrap().clone() +} + +pub fn run(rx: Receiver) { + let mut uploader = RecordUploader { + client: Client::new(), + api_server: crate::get_api_server( + Config::get_option("api-server"), + Config::get_option("custom-rendezvous-server"), + ), + filepath: Default::default(), + filename: Default::default(), + upload_size: Default::default(), + running: Default::default(), + last_send: Instant::now(), + }; + std::thread::spawn(move || loop { + if let Err(e) = match rx.recv() { + Ok(state) => match state { + RecordState::NewFile(filepath) => uploader.handle_new_file(filepath), + RecordState::NewFrame => { + if uploader.running { + uploader.handle_frame(false) + } else { + Ok(()) + } + } + RecordState::WriteTail => { + if uploader.running { + uploader.handle_tail() + } else { + Ok(()) + } + } + RecordState::RemoveFile => { + if uploader.running { + uploader.handle_remove() + } else { + Ok(()) + } + } + }, + Err(e) => { + log::trace!("upload thread stop:{}", e); + break; + } + } { + uploader.running = false; + log::error!("upload stop:{}", e); + } + }); +} + +struct RecordUploader { + client: Client, + api_server: String, + filepath: String, + filename: String, + upload_size: u64, + running: bool, + last_send: Instant, +} +impl RecordUploader { + fn send(&self, query: &Q, body: B) -> ResultType<()> + where + Q: Serialize + ?Sized, + B: Into, + { + match self + .client + .post(format!("{}/api/record", self.api_server)) + .query(query) + .body(body) + .send() + { + Ok(resp) => { + if let Ok(m) = resp.json::>() { + if let Some(e) = m.get("error") { + bail!(e.to_string()); + } + } + Ok(()) + } + Err(e) => bail!(e.to_string()), + } + } + + fn handle_new_file(&mut self, filepath: String) -> ResultType<()> { + match std::path::PathBuf::from(&filepath).file_name() { + Some(filename) => match filename.to_owned().into_string() { + Ok(filename) => { + self.filename = filename.clone(); + self.filepath = filepath.clone(); + self.upload_size = 0; + self.running = true; + self.last_send = Instant::now(); + self.send(&[("type", "new"), ("file", &filename)], Bytes::new())?; + Ok(()) + } + Err(_) => bail!("can't parse filename:{:?}", filename), + }, + None => bail!("can't parse filepath:{}", filepath), + } + } + + fn handle_frame(&mut self, flush: bool) -> ResultType<()> { + if !flush && self.last_send.elapsed() < SHOULD_SEND_TIME { + return Ok(()); + } + match File::open(&self.filepath) { + Ok(mut file) => match file.metadata() { + Ok(m) => { + let len = m.len(); + if len <= self.upload_size { + return Ok(()); + } + if !flush && len - self.upload_size < SHOULD_SEND_SIZE { + return Ok(()); + } + let mut buf = Vec::new(); + match file.seek(SeekFrom::Start(self.upload_size)) { + Ok(_) => match file.read_to_end(&mut buf) { + Ok(length) => { + self.send( + &[ + ("type", "part"), + ("file", &self.filename), + ("offset", &self.upload_size.to_string()), + ("length", &length.to_string()), + ], + buf, + )?; + self.upload_size = len; + self.last_send = Instant::now(); + Ok(()) + } + Err(e) => bail!(e.to_string()), + }, + Err(e) => bail!(e.to_string()), + } + } + Err(e) => bail!(e.to_string()), + }, + Err(e) => bail!(e.to_string()), + } + } + + fn handle_tail(&mut self) -> ResultType<()> { + self.handle_frame(true)?; + match File::open(&self.filepath) { + Ok(mut file) => { + let mut buf = vec![0u8; MAX_HEADER_LEN]; + match file.read(&mut buf) { + Ok(length) => { + buf.truncate(length); + self.send( + &[ + ("type", "tail"), + ("file", &self.filename), + ("offset", "0"), + ("length", &length.to_string()), + ], + buf, + )?; + log::info!("upload success, file:{}", self.filename); + Ok(()) + } + Err(e) => bail!(e.to_string()), + } + } + Err(e) => bail!(e.to_string()), + } + } + + fn handle_remove(&mut self) -> ResultType<()> { + self.send( + &[("type", "remove"), ("file", &self.filename)], + Bytes::new(), + )?; + Ok(()) + } +} diff --git a/src/lang/es.rs b/src/lang/es.rs index 1069b4905..17c3ddf07 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -399,6 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("hide_cm_tip", "Permitir ocultar solo si se aceptan sesiones a través de contraseña y usando contraseña permanente"), ("wayland_experiment_tip", "El soporte para Wayland está en fase experimental, por favor, use X11 si necesita acceso desatendido."), ("Right click to select tabs", "Clic derecho para seleccionar pestañas"), - ("Add to Address Book", ""), + ("Add to Address Book", "Añadir a la libreta de direcciones"), ].iter().cloned().collect(); } diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index ace70e1bd..6d2e92ae3 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -44,7 +44,7 @@ const ADDR_CAPTURE_FRAME_COUNTER: usize = ADDR_CAPTURE_WOULDBLOCK + size_of:: { @@ -622,7 +622,7 @@ pub mod client { async fn start_ipc_server_async(rx: mpsc::UnboundedReceiver) { use DataPortableService::*; let rx = Arc::new(tokio::sync::Mutex::new(rx)); - let postfix = IPC_PROFIX; + let postfix = IPC_SUFFIX; #[cfg(feature = "flutter")] let quick_support = { let args: Vec<_> = std::env::args().collect(); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 28b73cf7c..6d1235ed8 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -21,8 +21,9 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] use crate::portable_service::client::PORTABLE_SERVICE_RUNNING; +#[cfg(windows)] +use hbb_common::get_version_number; use hbb_common::{ - get_version_number, tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -481,22 +482,7 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); let codec_name = Encoder::current_hw_encoder_name(); - #[cfg(not(target_os = "ios"))] - let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { - Recorder::new(RecorderContext { - id: "local".to_owned(), - default_dir: crate::ui_interface::default_video_save_directory(), - filename: "".to_owned(), - width: c.width, - height: c.height, - codec_id: scrap::record::RecordCodecID::VP9, - }) - .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) - } else { - Default::default() - }; - #[cfg(target_os = "ios")] - let recorder: Arc>> = Default::default(); + let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -673,6 +659,53 @@ fn run(sp: GenericService) -> ResultType<()> { Ok(()) } +fn get_recorder( + width: usize, + height: usize, + codec_name: &Option, +) -> Arc>> { + #[cfg(not(target_os = "ios"))] + let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { + use crate::hbbs_http::record_upload; + use scrap::record::RecordCodecID::*; + + let tx = if record_upload::is_enable() { + let (tx, rx) = std::sync::mpsc::channel(); + record_upload::run(rx); + Some(tx) + } else { + None + }; + let codec_id = match codec_name { + Some(name) => { + if name.contains("264") { + H264 + } else { + H265 + } + } + None => VP9, + }; + Recorder::new(RecorderContext { + server: true, + id: Config::get_id(), + default_dir: crate::ui_interface::default_video_save_directory(), + filename: "".to_owned(), + width, + height, + codec_id, + tx, + }) + .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) + } else { + Default::default() + }; + #[cfg(target_os = "ios")] + let recorder: Arc>> = Default::default(); + + recorder +} + fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> ResultType<()> { let privacy_mode_id_2 = *PRIVACY_MODE_CONN_ID.lock().unwrap(); if privacy_mode_id != privacy_mode_id_2 { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 59082d00d..604d2e222 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -685,6 +685,7 @@ pub fn discover() { }); } +#[cfg(feature = "flutter")] pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> { HashMap::<&str, String>::from_iter([ ("id", id),