diff --git a/bun.lockb b/bun.lockb index 54ee432..f28aa37 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index e7f4d4a..ce7a3ee 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,52 @@ { - "name": "liveship", - "version": "0.0.0", - "description": "", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "tauri": "tauri" - }, - "license": "MIT", - "dependencies": { - "@tauri-apps/api": "^2.0.0-beta.13", - "@tauri-apps/plugin-dialog": "^2.0.0-beta.5", - "@tauri-apps/plugin-http": "^2.0.0-beta.5", - "@tauri-apps/plugin-shell": "^2.0.0-beta.6", - "@types/crypto-js": "^4.2.2", - "crypto-js": "^4.2.0", - "dayjs": "^1.11.11", - "jsonpath-plus": "^9.0.0", - "svelte-sonner": "^0.3.24", - "sveltekit-i18n": "^2.4.2" - }, - "devDependencies": { - "@iconify-json/bxs": "^1.1.10", - "@iconify-json/fluent": "^1.1.56", - "@iconify-json/icomoon-free": "^1.1.8", - "@iconify/tailwind": "^1.1.1", - "@sveltejs/adapter-static": "^3.0.2", - "@sveltejs/kit": "^2.5.16", - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "@tauri-apps/cli": "^2.0.0-beta.20", - "autoprefixer": "^10.4.19", - "daisyui": "^4.12.2", - "internal-ip": "^8.0.0", - "postcss": "^8.4.38", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.4", - "prettier-plugin-tailwindcss": "^0.5.14", - "svelte": "^5.0.0-next.155", - "svelte-check": "^3.8.0", - "tailwindcss": "^3.4.4", - "tslib": "^2.6.3", - "typescript": "^5.4.5", - "unplugin-icons": "^0.19.0", - "vite": "^5.3.1" - } + "name": "liveship", + "version": "0.0.0", + "description": "", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "tauri": "tauri" + }, + "license": "MIT", + "dependencies": { + "@tauri-apps/api": "^2.0.0-beta.13", + "@tauri-apps/plugin-dialog": "^2.0.0-beta.5", + "@tauri-apps/plugin-http": "^2.0.0-beta.5", + "@tauri-apps/plugin-os": "^2.0.0-beta.5", + "@tauri-apps/plugin-shell": "^2.0.0-beta.6", + "@types/crypto-js": "^4.2.2", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.11", + "jsonpath-plus": "^9.0.0", + "svelte-sonner": "^0.3.24", + "sveltekit-i18n": "^2.4.2" + }, + "devDependencies": { + "@iconify-json/bxs": "^1.1.10", + "@iconify-json/fluent": "^1.1.56", + "@iconify-json/icomoon-free": "^1.1.8", + "@iconify/tailwind": "^1.1.1", + "@sveltejs/adapter-static": "^3.0.2", + "@sveltejs/kit": "^2.5.16", + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tauri-apps/cli": "^2.0.0-beta.20", + "autoprefixer": "^10.4.19", + "daisyui": "^4.12.2", + "internal-ip": "^8.0.0", + "postcss": "^8.4.38", + "prettier": "^3.3.2", + "prettier-plugin-svelte": "^3.2.4", + "prettier-plugin-tailwindcss": "^0.5.14", + "svelte": "^5.0.0-next.155", + "svelte-check": "^3.8.0", + "tailwindcss": "^3.4.4", + "tslib": "^2.6.3", + "typescript": "^5.4.5", + "unplugin-icons": "^0.19.0", + "vite": "^5.3.1" + } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 47b855a..c2c5727 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -41,6 +41,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +dependencies = [ + "memchr", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -332,7 +341,7 @@ checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" dependencies = [ "anyhow", "arrayvec", - "log", + "log 0.4.21", "nom", "num-rational", "v_frame", @@ -882,7 +891,7 @@ checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ "cookie", "idna 0.3.0", - "log", + "log 0.4.21", "publicsuffix", "serde", "serde_derive", @@ -1303,7 +1312,7 @@ dependencies = [ "rustc_version", "toml 0.8.2", "vswhom", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -1751,7 +1760,7 @@ checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", - "log", + "log 0.4.21", "rustversion", "windows 0.48.0", ] @@ -1766,6 +1775,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getopts" version = "0.2.21" @@ -1992,6 +2011,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "handlebars" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb04af2006ea09d985fef82b81e0eb25337e51b691c76403332378a53d521edc" +dependencies = [ + "lazy_static 0.2.11", + "log 0.3.9", + "pest", + "quick-error 1.2.3", + "regex 0.2.11", + "serde", + "serde_json", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2043,7 +2077,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ - "log", + "log 0.4.21", "mac", "markup5ever", "proc-macro2", @@ -2431,6 +2465,22 @@ dependencies = [ "web-sys", ] +[[package]] +name = "interfaces" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec8f50a973916cac3da5057c986db05cd3346f38c78e9bc24f64cc9f6a3978f" +dependencies = [ + "bitflags 1.3.2", + "cc", + "handlebars", + "lazy_static 1.4.0", + "libc", + "nix 0.23.2", + "serde", + "serde_derive", +] + [[package]] name = "interpolate_name" version = "0.2.4" @@ -2530,7 +2580,7 @@ dependencies = [ "cfg-if", "combine", "jni-sys", - "log", + "log 0.4.21", "thiserror", "walkdir", "windows-sys 0.45.0", @@ -2601,6 +2651,12 @@ dependencies = [ "selectors 0.22.0", ] +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" + [[package]] name = "lazy_static" version = "1.4.0" @@ -2623,7 +2679,7 @@ dependencies = [ "gtk", "gtk-sys", "libappindicator-sys", - "log", + "log 0.4.21", ] [[package]] @@ -2704,7 +2760,7 @@ checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "liveship" -version = "0.1.19" +version = "0.1.20" dependencies = [ "anyhow", "async-trait", @@ -2721,10 +2777,12 @@ dependencies = [ "serde_json", "showfile", "strum", + "sysproxy", "tauri", "tauri-build", "tauri-plugin-dialog", "tauri-plugin-http", + "tauri-plugin-os", "tauri-plugin-shell", "tauri-plugin-single-instance", "tokio", @@ -2740,6 +2798,15 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.21", +] + [[package]] name = "log" version = "0.4.21" @@ -2801,7 +2868,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ - "log", + "log 0.4.21", "phf 0.10.1", "phf_codegen 0.10.0", "string_cache", @@ -2840,6 +2907,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.7.1" @@ -2917,7 +2993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ "libc", - "log", + "log 0.4.21", "openssl", "openssl-probe", "openssl-sys", @@ -2962,6 +3038,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.26.4" @@ -3357,6 +3446,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log 0.4.21", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.2.0" @@ -3470,6 +3570,12 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8" + [[package]] name = "phf" version = "0.8.0" @@ -3839,6 +3945,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-error" version = "2.0.1" @@ -3961,7 +4073,7 @@ dependencies = [ "itertools 0.12.1", "libc", "libfuzzer-sys", - "log", + "log 0.4.21", "maybe-rayon", "new_debug_unreachable", "noop_proc_macro", @@ -3988,7 +4100,7 @@ dependencies = [ "avif-serialize", "imgref", "loop9", - "quick-error", + "quick-error 2.0.1", "rav1e", "rayon", "rgb", @@ -4064,13 +4176,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +dependencies = [ + "aho-corasick 0.6.10", + "memchr", + "regex-syntax 0.5.6", + "thread_local 0.3.6", + "utf8-ranges", +] + [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.4", @@ -4091,11 +4216,20 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +dependencies = [ + "ucd-util", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -4142,7 +4276,7 @@ dependencies = [ "hyper-util", "ipnet", "js-sys", - "log", + "log 0.4.21", "mime", "native-tls", "once_cell", @@ -4167,7 +4301,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -4231,7 +4365,7 @@ dependencies = [ "gobject-sys", "gtk-sys", "js-sys", - "log", + "log 0.4.21", "objc", "objc-foundation", "objc_id", @@ -4328,7 +4462,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ - "log", + "log 0.4.21", "ring", "rustls-pki-types", "rustls-webpki", @@ -4387,7 +4521,7 @@ dependencies = [ "mime", "once_cell", "rand 0.8.5", - "regex", + "regex 1.10.5", "reqwest", "reqwest-middleware", "reqwest-retry", @@ -4518,7 +4652,7 @@ dependencies = [ "cssparser 0.27.2", "derive_more", "fxhash", - "log", + "log 0.4.21", "matches", "phf 0.8.0", "phf_codegen 0.8.0", @@ -4538,7 +4672,7 @@ dependencies = [ "cssparser 0.31.2", "derive_more", "fxhash", - "log", + "log 0.4.21", "new_debug_unreachable", "phf 0.10.1", "phf_codegen 0.10.0", @@ -4740,7 +4874,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "lazy_static", + "lazy_static 1.4.0", ] [[package]] @@ -4831,7 +4965,7 @@ dependencies = [ "core-graphics", "foreign-types 0.5.0", "js-sys", - "log", + "log 0.4.21", "objc2", "objc2-app-kit", "objc2-foundation", @@ -5016,6 +5150,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "sys-locale" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +dependencies = [ + "libc", +] + +[[package]] +name = "sysproxy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9707a79d3b95683aa5a9521e698ffd878b8fb289727c25a69157fb85d529ffff" +dependencies = [ + "interfaces", + "thiserror", + "winapi", + "winreg 0.10.1", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -5069,9 +5224,9 @@ dependencies = [ "gtk", "instant", "jni", - "lazy_static", + "lazy_static 1.4.0", "libc", - "log", + "log 0.4.21", "ndk", "ndk-context", "ndk-sys", @@ -5132,7 +5287,7 @@ dependencies = [ "http", "jni", "libc", - "log", + "log 0.4.21", "mime", "muda", "objc", @@ -5248,7 +5403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed4b22c59f7b04ae2a0bed8241aa715b41973c3f042c84aa67a1f4dc0174a8d" dependencies = [ "dunce", - "log", + "log 0.4.21", "raw-window-handle 0.6.2", "rfd", "serde", @@ -5286,7 +5441,7 @@ checksum = "9d5b9dc82eedca8daabeaa779be7e3a74dc1c07ab2339cbc071f3997a3b1b544" dependencies = [ "data-url", "http", - "regex", + "regex 1.10.5", "reqwest", "schemars", "serde", @@ -5299,6 +5454,24 @@ dependencies = [ "urlpattern", ] +[[package]] +name = "tauri-plugin-os" +version = "2.0.0-beta.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae3c8aeb113ce614cc36a43b1061fdf64381f7635d02c390052ce7579ec628" +dependencies = [ + "gethostname", + "log 0.4.21", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror", +] + [[package]] name = "tauri-plugin-shell" version = "2.0.0-beta.7" @@ -5306,10 +5479,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8675bf7ab71f571a99192d0685ae870eb7af1264bdbbb66a1d655609f6c7ebd" dependencies = [ "encoding_rs", - "log", + "log 0.4.21", "open", "os_pipe", - "regex", + "regex 1.10.5", "schemars", "serde", "serde_json", @@ -5326,7 +5499,7 @@ version = "2.0.0-beta.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ecafcc5214a5d3cd7a720c11e9c03cbd45ccaff721963485ec4ab481bdf4540" dependencies = [ - "log", + "log 0.4.21", "serde", "serde_json", "tauri", @@ -5364,7 +5537,7 @@ dependencies = [ "gtk", "http", "jni", - "log", + "log 0.4.21", "percent-encoding", "raw-window-handle 0.6.2", "softbuffer", @@ -5393,12 +5566,12 @@ dependencies = [ "infer", "json-patch", "kuchikiki", - "log", + "log 0.4.21", "memchr", "phf 0.11.2", "proc-macro2", "quote", - "regex", + "regex 1.10.5", "schemars", "semver", "serde", @@ -5477,6 +5650,15 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static 1.4.0", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -5743,7 +5925,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "log", + "log 0.4.21", "once_cell", "tracing-core", ] @@ -5757,10 +5939,10 @@ dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex 1.10.5", "sharded-slab", "smallvec", - "thread_local", + "thread_local 1.1.8", "tracing", "tracing-core", "tracing-log", @@ -5798,6 +5980,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd2fc5d32b590614af8b0a20d837f32eca055edd0bbead59a9cfe80858be003" + [[package]] name = "uds_windows" version = "1.1.0" @@ -5914,7 +6102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609" dependencies = [ "derive_more", - "regex", + "regex 1.10.5", "serde", "unic-ucd-ident", "url", @@ -5932,6 +6120,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +[[package]] +name = "utf8-ranges" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6050,7 +6244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", - "log", + "log 0.4.21", "once_cell", "proc-macro2", "quote", @@ -6134,7 +6328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12" dependencies = [ "dlib", - "log", + "log 0.4.21", "pkg-config", ] @@ -6635,6 +6829,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "winreg" version = "0.52.0" @@ -6803,7 +7006,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "regex", + "regex 1.10.5", "syn 1.0.109", "zvariant_utils", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 13c0d06..6344a8c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "liveship" -version = "0.1.19" +version = "0.1.20" description = "liveship is a compact and user-friendly live stream recording tool that captures live streams as video files." authors = ["jlvihv"] email = "imvihv@gmail.com" @@ -37,6 +37,8 @@ showfile = "0.1" ffmpeg-sidecar = "1.1" image = "0.25" rusty_ytdl = "0.7" +tauri-plugin-os = "2.0.0-beta.6" +sysproxy = "0.3.0" [profile.release] strip = true diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index b415843..8ec84e3 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -26,6 +26,8 @@ "url": "https://*" } ] - } + }, + "os:default", + "os:allow-platform" ] } diff --git a/src-tauri/src/ffmpeg.rs b/src-tauri/src/ffmpeg.rs index b976a23..fd735fd 100644 --- a/src-tauri/src/ffmpeg.rs +++ b/src-tauri/src/ffmpeg.rs @@ -3,7 +3,7 @@ pub use anyhow::Result; use ffmpeg_sidecar::download::{download_ffmpeg_package, ffmpeg_download_url, unpack_ffmpeg}; use std::process::{Child, Stdio}; -use crate::config::config_dir; +use crate::{config::config_dir, model::RecordingOption}; /// 给定 ffmpeg 命令,这里只负责执行 pub fn execute_ffmpeg_command(ffmpeg_command: Vec) -> 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 }); + } }